diff options
| author | Felix Hanley <felix@userspace.com.au> | 2018-11-20 05:44:30 +0000 |
|---|---|---|
| committer | Felix Hanley <felix@userspace.com.au> | 2018-11-20 05:44:30 +0000 |
| commit | 12f344f96a8225c93873aafa0f110babae267a0a (patch) | |
| tree | ac03e950244ea48d6e8f26b72f8d11ed94cded5b | |
| parent | 505c75b306322a22aef900aac636100eab556be9 (diff) | |
| download | query-12f344f96a8225c93873aafa0f110babae267a0a.tar.gz query-12f344f96a8225c93873aafa0f110babae267a0a.tar.bz2 | |
Use Node interface for JSON
| -rw-r--r-- | json/nav.go | 35 | ||||
| -rw-r--r-- | json/node.go | 123 | ||||
| -rw-r--r-- | jsonpath/parser.go | 53 | ||||
| -rw-r--r-- | jsonpath/parser_test.go | 5 | ||||
| -rw-r--r-- | jsonpath/selector.go | 18 | ||||
| -rw-r--r-- | node.go | 14 |
6 files changed, 125 insertions, 123 deletions
diff --git a/json/nav.go b/json/nav.go index c366966..a870573 100644 --- a/json/nav.go +++ b/json/nav.go @@ -1,8 +1,6 @@ package json import ( - "fmt" - base "src.userspace.com.au/query" ) @@ -16,20 +14,11 @@ func (a *NodeNavigator) Current() *Node { } func (a *NodeNavigator) NodeType() base.NodeType { - switch a.cur.Type { - case TextNode: - return base.TextNode - case DocumentNode: - return base.DocumentNode - case ElementNode: - return base.ElementNode - default: - panic(fmt.Sprintf("unknown node type %v", a.cur.Type)) - } + return a.cur.nodeType } func (a *NodeNavigator) LocalName() string { - return a.cur.Data + return a.cur.data } @@ -38,11 +27,11 @@ func (a *NodeNavigator) Prefix() string { } func (a *NodeNavigator) Value() string { - switch a.cur.Type { - case ElementNode: - return a.cur.InnerText() - case TextNode: - return a.cur.Data + switch a.cur.nodeType { + case base.ElementNode: + return a.cur.data + case base.TextNode: + return a.cur.data } return "" } @@ -57,7 +46,7 @@ func (a *NodeNavigator) MoveToRoot() { } func (a *NodeNavigator) MoveToParent() bool { - if n := a.cur.Parent; n != nil { + if n := a.cur.parent; n != nil { a.cur = n return true } @@ -69,7 +58,7 @@ func (x *NodeNavigator) MoveToNextAttribute() bool { } func (a *NodeNavigator) MoveToChild() bool { - if n := a.cur.FirstChild; n != nil { + if n := a.cur.firstChild; n != nil { a.cur = n return true } @@ -77,7 +66,7 @@ func (a *NodeNavigator) MoveToChild() bool { } func (a *NodeNavigator) MoveToFirst() bool { - for n := a.cur.PrevSibling; n != nil; n = n.PrevSibling { + for n := a.cur.prevSibling; n != nil; n = n.prevSibling { a.cur = n } return true @@ -88,7 +77,7 @@ func (a *NodeNavigator) String() string { } func (a *NodeNavigator) MoveToNext() bool { - if n := a.cur.NextSibling; n != nil { + if n := a.cur.nextSibling; n != nil { a.cur = n return true } @@ -96,7 +85,7 @@ func (a *NodeNavigator) MoveToNext() bool { } func (a *NodeNavigator) MoveToPrevious() bool { - if n := a.cur.PrevSibling; n != nil { + if n := a.cur.prevSibling; n != nil { a.cur = n return true } diff --git a/json/node.go b/json/node.go index 67043a1..56d0c64 100644 --- a/json/node.go +++ b/json/node.go @@ -7,40 +7,48 @@ import ( "io" "sort" "strconv" -) - -// A NodeType is the type of a Node. -type NodeType uint -const ( - // DocumentNode the root of the document tree. - DocumentNode NodeType = iota - // ElementNode is an element. - ElementNode - // TextNode is the text content of a node. - TextNode + base "src.userspace.com.au/query" ) -var NodeNames = map[NodeType]string{ - DocumentNode: "DocumentNode", - ElementNode: "ElementNode", - TextNode: "TextNode", -} - -// A Node consists of a NodeType and some Data (tag name for +// 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 + parent, prevSibling, nextSibling, firstChild, lastChild *Node - Type NodeType - Data string - DataType string + nodeType base.NodeType + data string + dataType string level int } +func (n *Node) Parent() base.Node { + if n.parent == nil { + return nil + } + return n.parent +} +func (n *Node) NextSibling() base.Node { + if n.nextSibling == nil { + return nil + } + return n.nextSibling +} +func (n *Node) FirstChild() base.Node { + if n.firstChild == nil { + return nil + } + return n.firstChild +} +func (n *Node) PrevSibling() base.Node { return n.prevSibling } +func (n *Node) LastChild() base.Node { return n.lastChild } +func (n *Node) Type() base.NodeType { return base.NodeType(n.nodeType) } +func (n *Node) DataType() string { return n.dataType } +func (n *Node) Attr() []base.Attribute { return nil } + func (n Node) String() string { - return fmt.Sprintf("[%s] %s(%s)", NodeNames[n.Type], n.DataType, n.Data) + return fmt.Sprintf("[%s] %s(%s)", base.NodeNames[n.nodeType], n.dataType, n.data) } func (n Node) PrintTree(level int) { @@ -48,7 +56,7 @@ func (n Node) PrintTree(level int) { fmt.Printf(" ") } fmt.Println(n) - for _, c := range n.ChildNodes() { + for c := n.firstChild; c != nil; c = c.nextSibling { c.PrintTree(level + 1) } } @@ -56,34 +64,35 @@ func (n Node) PrintTree(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 { + for nn := n.firstChild; nn != nil; nn = nn.nextSibling { a = append(a, nn) } return a } +// Data gets the value of the node and all its child nodes. +func (n *Node) Data() string { + return n.data +} + // 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 + if n.nodeType == base.TextNode { + buf.WriteString(n.data) + } else { + for child := n.firstChild; child != nil; child = child.nextSibling { + buf.WriteString(child.InnerText()) } } - 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 { + for nn := n.firstChild; nn != nil; nn = nn.nextSibling { + if nn.data == name { return nn } } @@ -93,22 +102,22 @@ func (n *Node) SelectElement(name string) *Node { 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 + 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 + 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 + t := top.lastChild + t.nextSibling = n + n.prevSibling = t + top.lastChild = n } } } @@ -118,33 +127,31 @@ func parseValue(x interface{}, top *Node, level int) { switch v := x.(type) { case []interface{}: for _, vv := range v { - n := &Node{Type: ElementNode, level: level, DataType: "arrayitem"} + n := &Node{nodeType: base.ElementNode, level: level, dataType: "arrayitem"} 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, DataType: "object"} + n := &Node{data: key, nodeType: base.ElementNode, level: level, dataType: "object"} addNode(n) parseValue(v[key], n, level+1) } case string: - n := &Node{Data: v, Type: TextNode, level: level, DataType: "string"} + n := &Node{data: v, nodeType: base.TextNode, level: level, dataType: "string"} addNode(n) case float64: s := strconv.FormatFloat(v, 'f', -1, 64) - n := &Node{Data: s, Type: TextNode, level: level, DataType: "number"} + n := &Node{data: s, nodeType: base.TextNode, level: level, dataType: "number"} addNode(n) case bool: s := strconv.FormatBool(v) - n := &Node{Data: s, Type: TextNode, level: level, DataType: "boolean"} + n := &Node{data: s, nodeType: base.TextNode, level: level, dataType: "boolean"} addNode(n) } } @@ -156,7 +163,7 @@ func Parse(r io.Reader) (*Node, error) { if err := decoder.Decode(&v); err != nil { return nil, err } - doc := &Node{Type: DocumentNode} + doc := &Node{nodeType: base.DocumentNode} parseValue(v, doc, 1) return doc, nil } diff --git a/jsonpath/parser.go b/jsonpath/parser.go index 5f0cd3a..3631f65 100644 --- a/jsonpath/parser.go +++ b/jsonpath/parser.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "src.userspace.com.au/query/json" + base "src.userspace.com.au/query" "src.userspace.com.au/query/lexer" ) @@ -162,58 +162,52 @@ func (p *Parser) parsePredicateExprSelector() (Selector, error) { } // rootSelector checks node is root -func rootSelector(n *json.Node) bool { +func rootSelector(n base.Node) bool { result := false - if n.Parent != nil && n.Parent.Type == json.DocumentNode { + parent := n.Parent() + if parent != nil && parent.Type() == base.DocumentNode { result = true } else { - result = (n.Type == json.DocumentNode) + result = (n.Type() == base.DocumentNode) } return result } // wildcardSelector returns true -func wildcardSelector(n *json.Node) bool { +func wildcardSelector(n base.Node) bool { return true } // childSelector creates a selector for c being a child of p func childSelector(p, c Selector) Selector { - return func(n *json.Node) bool { - /* - for child := n.FirstChild; child != nil; child = child.NextSibling { - if p(n) && c(child) { - return true - } - } - return false - */ - result := (c(n) && n.Parent != nil && p(n.Parent)) + return func(n base.Node) bool { + parent := n.Parent() + result := (c(n) && parent != nil && p(parent)) return result } } // nameSelector generates selector for object key == k func nameSelector(k string) Selector { - return func(n *json.Node) bool { - result := (n.Type == json.ElementNode && n.Data == k) + return func(n base.Node) bool { + result := (n.Type() == base.ElementNode && n.Data() == k) return result } } // recursiveSelector matches any node below which matches a func recursiveSelector(a Selector) Selector { - return func(n *json.Node) bool { - if n.Type != json.ElementNode { + return func(n base.Node) bool { + if n.Type() != base.ElementNode { return false } return hasRecursiveMatch(n, a) } } -func hasRecursiveMatch(n *json.Node, a Selector) bool { - for c := n.FirstChild; c != nil; c = c.NextSibling { - if a(c) || (c.Type == json.ElementNode && hasRecursiveMatch(c, a)) { +func hasRecursiveMatch(n base.Node, a Selector) bool { + for c := n.FirstChild(); c != nil; c = c.NextSibling() { + if a(c) || (c.Type() == base.ElementNode && hasRecursiveMatch(c, a)) { return true } } @@ -222,17 +216,18 @@ func hasRecursiveMatch(n *json.Node, a Selector) bool { // arrayIndexSelector generates selector for node being idx index of parent func arrayIndexSelector(idx int64) Selector { - return func(n *json.Node) bool { - if n.DataType != "arrayitem" { + return func(n base.Node) bool { + if n.DataType() != "arrayitem" { return false } - if n.Parent == nil { + parent := n.Parent() + + if parent == nil { return false } - parent := n.Parent i := int64(0) - for c := parent.FirstChild; c != nil && i <= idx; c = c.NextSibling { + for c := parent.FirstChild(); c != nil && i <= idx; c = c.NextSibling() { if i == idx && c == n { return true } @@ -244,9 +239,9 @@ func arrayIndexSelector(idx int64) Selector { // typeSelector matches a node with type t func typeSelector(t string) Selector { - return func(n *json.Node) bool { + return func(n base.Node) bool { // FIXME - if n.DataType == t { + if n.DataType() == t { return true } return false diff --git a/jsonpath/parser_test.go b/jsonpath/parser_test.go index 2406651..dd86944 100644 --- a/jsonpath/parser_test.go +++ b/jsonpath/parser_test.go @@ -1,7 +1,6 @@ package jsonpath import ( - //"fmt" "strings" "testing" @@ -84,9 +83,9 @@ func TestParse(t *testing.T) { } if tt.expect == "" && actualText != "" { - t.Fatalf("MatchFirst(%s) => %s, expected nothing", tt.src, actualText) + t.Fatalf("MatchFirst(%s, %s) => %s, expected nothing", tt.path, tt.src, actualText) } else if actualText != tt.expect { - t.Fatalf("MatchFirst(%s) => %s, expected %s", tt.src, actualText, tt.expect) + t.Fatalf("MatchFirst(%s, %s) => %s, expected %s", tt.path, tt.src, actualText, tt.expect) } } } diff --git a/jsonpath/selector.go b/jsonpath/selector.go index 71c3ed2..4728566 100644 --- a/jsonpath/selector.go +++ b/jsonpath/selector.go @@ -1,23 +1,23 @@ package jsonpath import ( - "src.userspace.com.au/query/json" + base "src.userspace.com.au/query" ) -type Selector func(*json.Node) bool +type Selector func(base.Node) bool // MatchAll returns a slice of the nodes that match the selector, // from n and its children. -func (s Selector) MatchAll(n *json.Node) []*json.Node { +func (s Selector) MatchAll(n base.Node) []base.Node { return s.matchAllInto(n, nil) } -func (s Selector) matchAllInto(n *json.Node, storage []*json.Node) []*json.Node { +func (s Selector) matchAllInto(n base.Node, storage []base.Node) []base.Node { if s(n) { storage = append(storage, n) } - for child := n.FirstChild; child != nil; child = child.NextSibling { + for child := n.FirstChild(); child != nil; child = child.NextSibling() { storage = s.matchAllInto(child, storage) } @@ -25,17 +25,17 @@ func (s Selector) matchAllInto(n *json.Node, storage []*json.Node) []*json.Node } // Match returns true if the node matches the selector. -func (s Selector) Match(n *json.Node) bool { +func (s Selector) Match(n base.Node) bool { return s(n) } // MatchFirst returns the first node that matches s, from n and its children. -func (s Selector) MatchFirst(n *json.Node) *json.Node { +func (s Selector) MatchFirst(n base.Node) base.Node { if s.Match(n) { return n } - for c := n.FirstChild; c != nil; c = c.NextSibling { + for c := n.FirstChild(); c != nil; c = c.NextSibling() { m := s.MatchFirst(c) if m != nil { return m @@ -45,7 +45,7 @@ func (s Selector) MatchFirst(n *json.Node) *json.Node { } // Filter returns the nodes in nodes that match the selector. -func (s Selector) Filter(nodes []*json.Node) (result []*json.Node) { +func (s Selector) Filter(nodes []base.Node) (result []base.Node) { for _, n := range nodes { if s(n) { result = append(result, n) @@ -15,9 +15,19 @@ const ( DoctypeNode = NodeType(html.DoctypeNode) AttributeNode = 100 AnyNode = 101 - // Add json node types ) +var NodeNames = map[NodeType]string{ + ErrorNode: "ErrorNode", + DocumentNode: "DocumentNode", + ElementNode: "ElementNode", + TextNode: "TextNode", + CommentNode: "CommentNode", + DoctypeNode: "DoctypeNode", + AttributeNode: "AttributeNode", + AnyNode: "AnyNode", +} + type Node interface { Parent() Node FirstChild() Node @@ -26,6 +36,8 @@ type Node interface { NextSibling() Node Type() NodeType Data() string + InnerText() string + DataType() string Attr() []Attribute } |
