bettercap/vendor/github.com/antchfx/jsonquery/node.go

157 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package jsonquery
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"sort"
"strconv"
)
// A NodeType is the type of a Node.
type NodeType uint
const (
// DocumentNode is a document object that, as the root of the document tree,
// provides access to the entire XML document.
DocumentNode NodeType = iota
// ElementNode is an element.
ElementNode
// TextNode is the text content of a node.
TextNode
)
// A Node consists of a NodeType and some Data (tag name for
// element nodes, content for text) and are part of a tree of Nodes.
type Node struct {
Parent, PrevSibling, NextSibling, FirstChild, LastChild *Node
Type NodeType
Data string
level int
}
// ChildNodes gets all child nodes of the node.
func (n *Node) ChildNodes() []*Node {
var a []*Node
for nn := n.FirstChild; nn != nil; nn = nn.NextSibling {
a = append(a, nn)
}
return a
}
// InnerText gets the value of the node and all its child nodes.
func (n *Node) InnerText() string {
var output func(*bytes.Buffer, *Node)
output = func(buf *bytes.Buffer, n *Node) {
if n.Type == TextNode {
buf.WriteString(n.Data)
return
}
for child := n.FirstChild; child != nil; child = child.NextSibling {
output(buf, child)
}
}
var buf bytes.Buffer
output(&buf, n)
return buf.String()
}
// SelectElement finds the first of child elements with the
// specified name.
func (n *Node) SelectElement(name string) *Node {
for nn := n.FirstChild; nn != nil; nn = nn.NextSibling {
if nn.Data == name {
return nn
}
}
return nil
}
// LoadURL loads the JSON document from the specified URL.
func LoadURL(url string) (*Node, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return Parse(resp.Body)
}
func parseValue(x interface{}, top *Node, level int) {
addNode := func(n *Node) {
if n.level == top.level {
top.NextSibling = n
n.PrevSibling = top
n.Parent = top.Parent
if top.Parent != nil {
top.Parent.LastChild = n
}
} else if n.level > top.level {
n.Parent = top
if top.FirstChild == nil {
top.FirstChild = n
top.LastChild = n
} else {
t := top.LastChild
t.NextSibling = n
n.PrevSibling = t
top.LastChild = n
}
}
}
switch v := x.(type) {
case []interface{}:
for _, vv := range v {
n := &Node{Type: ElementNode, level: level}
addNode(n)
parseValue(vv, n, level+1)
}
case map[string]interface{}:
// The Gos map iteration order is random.
// (https://blog.golang.org/go-maps-in-action#Iteration-order)
var keys []string
for key := range v {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
n := &Node{Data: key, Type: ElementNode, level: level}
addNode(n)
parseValue(v[key], n, level+1)
}
case string:
n := &Node{Data: v, Type: TextNode, level: level}
addNode(n)
case float64:
s := strconv.FormatFloat(v, 'f', -1, 64)
n := &Node{Data: s, Type: TextNode, level: level}
addNode(n)
case bool:
s := strconv.FormatBool(v)
n := &Node{Data: s, Type: TextNode, level: level}
addNode(n)
}
}
func parse(b []byte) (*Node, error) {
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return nil, err
}
doc := &Node{Type: DocumentNode}
parseValue(v, doc, 1)
return doc, nil
}
// Parse JSON document.
func Parse(r io.Reader) (*Node, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
return parse(b)
}