summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2018-11-20 05:44:30 +0000
committerFelix Hanley <felix@userspace.com.au>2018-11-20 05:44:30 +0000
commit12f344f96a8225c93873aafa0f110babae267a0a (patch)
treeac03e950244ea48d6e8f26b72f8d11ed94cded5b
parent505c75b306322a22aef900aac636100eab556be9 (diff)
downloadquery-12f344f96a8225c93873aafa0f110babae267a0a.tar.gz
query-12f344f96a8225c93873aafa0f110babae267a0a.tar.bz2
Use Node interface for JSON
-rw-r--r--json/nav.go35
-rw-r--r--json/node.go123
-rw-r--r--jsonpath/parser.go53
-rw-r--r--jsonpath/parser_test.go5
-rw-r--r--jsonpath/selector.go18
-rw-r--r--node.go14
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)
diff --git a/node.go b/node.go
index 6a091e3..61650fb 100644
--- a/node.go
+++ b/node.go
@@ -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
}