mirror of
https://github.com/bettercap/bettercap
synced 2025-07-06 04:52:10 -07:00
157 lines
3.4 KiB
Go
157 lines
3.4 KiB
Go
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 Go’s 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)
|
||
}
|