summaryrefslogtreecommitdiff
path: root/vendor/github.com/exponent-io/jsonpath
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/exponent-io/jsonpath')
-rw-r--r--vendor/github.com/exponent-io/jsonpath/LICENSE21
-rw-r--r--vendor/github.com/exponent-io/jsonpath/README.md66
-rw-r--r--vendor/github.com/exponent-io/jsonpath/decoder.go210
-rw-r--r--vendor/github.com/exponent-io/jsonpath/path.go67
-rw-r--r--vendor/github.com/exponent-io/jsonpath/pathaction.go61
5 files changed, 425 insertions, 0 deletions
diff --git a/vendor/github.com/exponent-io/jsonpath/LICENSE b/vendor/github.com/exponent-io/jsonpath/LICENSE
new file mode 100644
index 000000000..541977250
--- /dev/null
+++ b/vendor/github.com/exponent-io/jsonpath/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Exponent Labs LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/exponent-io/jsonpath/README.md b/vendor/github.com/exponent-io/jsonpath/README.md
new file mode 100644
index 000000000..382fb3138
--- /dev/null
+++ b/vendor/github.com/exponent-io/jsonpath/README.md
@@ -0,0 +1,66 @@
+[![GoDoc](https://godoc.org/github.com/exponent-io/jsonpath?status.svg)](https://godoc.org/github.com/exponent-io/jsonpath)
+[![Build Status](https://travis-ci.org/exponent-io/jsonpath.svg?branch=master)](https://travis-ci.org/exponent-io/jsonpath)
+
+# jsonpath
+
+This package extends the [json.Decoder](https://golang.org/pkg/encoding/json/#Decoder) to support navigating a stream of JSON tokens. You should be able to use this extended Decoder places where a json.Decoder would have been used.
+
+This Decoder has the following enhancements...
+ * The [Scan](https://godoc.org/github.com/exponent-io/jsonpath/#Decoder.Scan) method supports scanning a JSON stream while extracting particular values along the way using [PathActions](https://godoc.org/github.com/exponent-io/jsonpath#PathActions).
+ * The [SeekTo](https://godoc.org/github.com/exponent-io/jsonpath#Decoder.SeekTo) method supports seeking forward in a JSON token stream to a particular path.
+ * The [Path](https://godoc.org/github.com/exponent-io/jsonpath#Decoder.Path) method returns the path of the most recently parsed token.
+ * The [Token](https://godoc.org/github.com/exponent-io/jsonpath#Decoder.Token) method has been modified to distinguish between strings that are object keys and strings that are values. Object key strings are returned as the [KeyString](https://godoc.org/github.com/exponent-io/jsonpath#KeyString) type rather than a native string.
+
+## Installation
+
+ go get -u github.com/exponent-io/jsonpath
+
+## Example Usage
+
+#### SeekTo
+
+```go
+import "github.com/exponent-io/jsonpath"
+
+var j = []byte(`[
+ {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
+ {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}}
+]`)
+
+w := json.NewDecoder(bytes.NewReader(j))
+var v interface{}
+
+w.SeekTo(1, "Point", "G")
+w.Decode(&v) // v is 218
+```
+
+#### Scan with PathActions
+
+```go
+var j = []byte(`{"colors":[
+ {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10, "A": 58}},
+ {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255, "A": 231}}
+]}`)
+
+var actions PathActions
+
+// Extract the value at Point.A
+actions.Add(func(d *Decoder) error {
+ var alpha int
+ err := d.Decode(&alpha)
+ fmt.Printf("Alpha: %v\n", alpha)
+ return err
+}, "Point", "A")
+
+w := NewDecoder(bytes.NewReader(j))
+w.SeekTo("colors", 0)
+
+var ok = true
+var err error
+for ok {
+ ok, err = w.Scan(&actions)
+ if err != nil && err != io.EOF {
+ panic(err)
+ }
+}
+```
diff --git a/vendor/github.com/exponent-io/jsonpath/decoder.go b/vendor/github.com/exponent-io/jsonpath/decoder.go
new file mode 100644
index 000000000..31de46c73
--- /dev/null
+++ b/vendor/github.com/exponent-io/jsonpath/decoder.go
@@ -0,0 +1,210 @@
+package jsonpath
+
+import (
+ "encoding/json"
+ "io"
+)
+
+// KeyString is returned from Decoder.Token to represent each key in a JSON object value.
+type KeyString string
+
+// Decoder extends the Go runtime's encoding/json.Decoder to support navigating in a stream of JSON tokens.
+type Decoder struct {
+ json.Decoder
+
+ path JsonPath
+ context jsonContext
+}
+
+// NewDecoder creates a new instance of the extended JSON Decoder.
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{Decoder: *json.NewDecoder(r)}
+}
+
+// SeekTo causes the Decoder to move forward to a given path in the JSON structure.
+//
+// The path argument must consist of strings or integers. Each string specifies an JSON object key, and
+// each integer specifies an index into a JSON array.
+//
+// Consider the JSON structure
+//
+// { "a": [0,"s",12e4,{"b":0,"v":35} ] }
+//
+// SeekTo("a",3,"v") will move to the value referenced by the "a" key in the current object,
+// followed by a move to the 4th value (index 3) in the array, followed by a move to the value at key "v".
+// In this example, a subsequent call to the decoder's Decode() would unmarshal the value 35.
+//
+// SeekTo returns a boolean value indicating whether a match was found.
+//
+// Decoder is intended to be used with a stream of tokens. As a result it navigates forward only.
+func (d *Decoder) SeekTo(path ...interface{}) (bool, error) {
+
+ if len(path) == 0 {
+ return len(d.path) == 0, nil
+ }
+ last := len(path) - 1
+ if i, ok := path[last].(int); ok {
+ path[last] = i - 1
+ }
+
+ for {
+ if d.path.Equal(path) {
+ return true, nil
+ }
+ _, err := d.Token()
+ if err == io.EOF {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ }
+}
+
+// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v. This is
+// equivalent to encoding/json.Decode().
+func (d *Decoder) Decode(v interface{}) error {
+ switch d.context {
+ case objValue:
+ d.context = objKey
+ break
+ case arrValue:
+ d.path.incTop()
+ break
+ }
+ return d.Decoder.Decode(v)
+}
+
+// Path returns a slice of string and/or int values representing the path from the root of the JSON object to the
+// position of the most-recently parsed token.
+func (d *Decoder) Path() JsonPath {
+ p := make(JsonPath, len(d.path))
+ copy(p, d.path)
+ return p
+}
+
+// Token is equivalent to the Token() method on json.Decoder. The primary difference is that it distinguishes
+// between strings that are keys and and strings that are values. String tokens that are object keys are returned as a
+// KeyString rather than as a native string.
+func (d *Decoder) Token() (json.Token, error) {
+ t, err := d.Decoder.Token()
+ if err != nil {
+ return t, err
+ }
+
+ if t == nil {
+ switch d.context {
+ case objValue:
+ d.context = objKey
+ break
+ case arrValue:
+ d.path.incTop()
+ break
+ }
+ return t, err
+ }
+
+ switch t := t.(type) {
+ case json.Delim:
+ switch t {
+ case json.Delim('{'):
+ if d.context == arrValue {
+ d.path.incTop()
+ }
+ d.path.push("")
+ d.context = objKey
+ break
+ case json.Delim('}'):
+ d.path.pop()
+ d.context = d.path.inferContext()
+ break
+ case json.Delim('['):
+ if d.context == arrValue {
+ d.path.incTop()
+ }
+ d.path.push(-1)
+ d.context = arrValue
+ break
+ case json.Delim(']'):
+ d.path.pop()
+ d.context = d.path.inferContext()
+ break
+ }
+ case float64, json.Number, bool:
+ switch d.context {
+ case objValue:
+ d.context = objKey
+ break
+ case arrValue:
+ d.path.incTop()
+ break
+ }
+ break
+ case string:
+ switch d.context {
+ case objKey:
+ d.path.nameTop(t)
+ d.context = objValue
+ return KeyString(t), err
+ case objValue:
+ d.context = objKey
+ case arrValue:
+ d.path.incTop()
+ }
+ break
+ }
+
+ return t, err
+}
+
+// Scan moves forward over the JSON stream consuming all the tokens at the current level (current object, current array)
+// invoking each matching PathAction along the way.
+//
+// Scan returns true if there are more contiguous values to scan (for example in an array).
+func (d *Decoder) Scan(ext *PathActions) (bool, error) {
+
+ rootPath := d.Path()
+
+ // If this is an array path, increment the root path in our local copy.
+ if rootPath.inferContext() == arrValue {
+ rootPath.incTop()
+ }
+
+ for {
+ // advance the token position
+ _, err := d.Token()
+ if err != nil {
+ return false, err
+ }
+
+ match:
+ var relPath JsonPath
+
+ // capture the new JSON path
+ path := d.Path()
+
+ if len(path) > len(rootPath) {
+ // capture the path relative to where the scan started
+ relPath = path[len(rootPath):]
+ } else {
+ // if the path is not longer than the root, then we are done with this scan
+ // return boolean flag indicating if there are more items to scan at the same level
+ return d.Decoder.More(), nil
+ }
+
+ // match the relative path against the path actions
+ if node := ext.node.match(relPath); node != nil {
+ if node.action != nil {
+ // we have a match so execute the action
+ err = node.action(d)
+ if err != nil {
+ return d.Decoder.More(), err
+ }
+ // The action may have advanced the decoder. If we are in an array, advancing it further would
+ // skip tokens. So, if we are scanning an array, jump to the top without advancing the token.
+ if d.path.inferContext() == arrValue && d.Decoder.More() {
+ goto match
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/exponent-io/jsonpath/path.go b/vendor/github.com/exponent-io/jsonpath/path.go
new file mode 100644
index 000000000..d7db2ad33
--- /dev/null
+++ b/vendor/github.com/exponent-io/jsonpath/path.go
@@ -0,0 +1,67 @@
+// Extends the Go runtime's json.Decoder enabling navigation of a stream of json tokens.
+package jsonpath
+
+import "fmt"
+
+type jsonContext int
+
+const (
+ none jsonContext = iota
+ objKey
+ objValue
+ arrValue
+)
+
+// AnyIndex can be used in a pattern to match any array index.
+const AnyIndex = -2
+
+// JsonPath is a slice of strings and/or integers. Each string specifies an JSON object key, and
+// each integer specifies an index into a JSON array.
+type JsonPath []interface{}
+
+func (p *JsonPath) push(n interface{}) { *p = append(*p, n) }
+func (p *JsonPath) pop() { *p = (*p)[:len(*p)-1] }
+
+// increment the index at the top of the stack (must be an array index)
+func (p *JsonPath) incTop() { (*p)[len(*p)-1] = (*p)[len(*p)-1].(int) + 1 }
+
+// name the key at the top of the stack (must be an object key)
+func (p *JsonPath) nameTop(n string) { (*p)[len(*p)-1] = n }
+
+// infer the context from the item at the top of the stack
+func (p *JsonPath) inferContext() jsonContext {
+ if len(*p) == 0 {
+ return none
+ }
+ t := (*p)[len(*p)-1]
+ switch t.(type) {
+ case string:
+ return objKey
+ case int:
+ return arrValue
+ default:
+ panic(fmt.Sprintf("Invalid stack type %T", t))
+ }
+}
+
+// Equal tests for equality between two JsonPath types.
+func (p *JsonPath) Equal(o JsonPath) bool {
+ if len(*p) != len(o) {
+ return false
+ }
+ for i, v := range *p {
+ if v != o[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func (p *JsonPath) HasPrefix(o JsonPath) bool {
+ for i, v := range o {
+ if v != (*p)[i] {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/exponent-io/jsonpath/pathaction.go b/vendor/github.com/exponent-io/jsonpath/pathaction.go
new file mode 100644
index 000000000..497ed686c
--- /dev/null
+++ b/vendor/github.com/exponent-io/jsonpath/pathaction.go
@@ -0,0 +1,61 @@
+package jsonpath
+
+// pathNode is used to construct a trie of paths to be matched
+type pathNode struct {
+ matchOn interface{} // string, or integer
+ childNodes []pathNode
+ action DecodeAction
+}
+
+// match climbs the trie to find a node that matches the given JSON path.
+func (n *pathNode) match(path JsonPath) *pathNode {
+ var node *pathNode = n
+ for _, ps := range path {
+ found := false
+ for i, n := range node.childNodes {
+ if n.matchOn == ps {
+ node = &node.childNodes[i]
+ found = true
+ break
+ } else if _, ok := ps.(int); ok && n.matchOn == AnyIndex {
+ node = &node.childNodes[i]
+ found = true
+ break
+ }
+ }
+ if !found {
+ return nil
+ }
+ }
+ return node
+}
+
+// PathActions represents a collection of DecodeAction functions that should be called at certain path positions
+// when scanning the JSON stream. PathActions can be created once and used many times in one or more JSON streams.
+type PathActions struct {
+ node pathNode
+}
+
+// DecodeAction handlers are called by the Decoder when scanning objects. See PathActions.Add for more detail.
+type DecodeAction func(d *Decoder) error
+
+// Add specifies an action to call on the Decoder when the specified path is encountered.
+func (je *PathActions) Add(action DecodeAction, path ...interface{}) {
+
+ var node *pathNode = &je.node
+ for _, ps := range path {
+ found := false
+ for i, n := range node.childNodes {
+ if n.matchOn == ps {
+ node = &node.childNodes[i]
+ found = true
+ break
+ }
+ }
+ if !found {
+ node.childNodes = append(node.childNodes, pathNode{matchOn: ps})
+ node = &node.childNodes[len(node.childNodes)-1]
+ }
+ }
+ node.action = action
+}