1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
// Tideland Go Library - Generic JSON Processor
//
// Copyright (C) 2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.
package gjp
//--------------------
// IMPORTS
//--------------------
import (
"encoding/json"
"github.com/tideland/golib/errors"
"github.com/tideland/golib/stringex"
)
//--------------------
// DOCUMENT
//--------------------
// PathValue is the combination of path and value.
type PathValue struct {
Path string
Value Value
}
// PathValues contains a number of path/value combinations.
type PathValues []PathValue
// ValueProcessor describes a function for the processing of
// values while iterating over a document.
type ValueProcessor func(path string, value Value) error
// Document represents one JSON document.
type Document interface {
json.Marshaler
// Length returns the number of elements for the given path.
Length(path string) int
// SetValueAt sets the value at the given path.
SetValueAt(path string, value interface{}) error
// ValueAt returns the addressed value.
ValueAt(path string) Value
// Clear removes the so far build document data.
Clear()
// Query allows to find pathes matching a given pattern.
Query(pattern string) (PathValues, error)
// Process iterates over a document and processes its values.
// There's no order, so nesting into an embedded document or
// list may come earlier than higher level paths.
Process(processor ValueProcessor) error
}
// document implements Document.
type document struct {
separator string
root interface{}
}
// Parse reads a raw document and returns it as
// accessible document.
func Parse(data []byte, separator string) (Document, error) {
var root interface{}
err := json.Unmarshal(data, &root)
if err != nil {
return nil, errors.Annotate(err, ErrUnmarshalling, errorMessages)
}
return &document{
separator: separator,
root: root,
}, nil
}
// NewDocument creates a new empty document.
func NewDocument(separator string) Document {
return &document{
separator: separator,
}
}
// Length implements Document.
func (d *document) Length(path string) int {
n, err := valueAt(d.root, splitPath(path, d.separator))
if err != nil {
return -1
}
// Check if object or array.
o, ok := isObject(n)
if ok {
return len(o)
}
a, ok := isArray(n)
if ok {
return len(a)
}
return 1
}
// SetValueAt implements Document.
func (d *document) SetValueAt(path string, value interface{}) error {
parts := splitPath(path, d.separator)
root, err := setValueAt(d.root, value, parts)
if err != nil {
return err
}
d.root = root
return nil
}
// ValueAt implements Document.
func (d *document) ValueAt(path string) Value {
n, err := valueAt(d.root, splitPath(path, d.separator))
return &value{n, err}
}
// Clear implements Document.
func (d *document) Clear() {
d.root = nil
}
// Query implements Document.
func (d *document) Query(pattern string) (PathValues, error) {
pvs := PathValues{}
err := d.Process(func(path string, value Value) error {
if stringex.Matches(pattern, path, false) {
pvs = append(pvs, PathValue{
Path: path,
Value: value,
})
}
return nil
})
return pvs, err
}
// Process implements Document.
func (d *document) Process(processor ValueProcessor) error {
return process(d.root, []string{}, d.separator, processor)
}
// MarshalJSON implements json.Marshaler.
func (d *document) MarshalJSON() ([]byte, error) {
return json.Marshal(d.root)
}
// EOF
|