Better interface
This commit is contained in:
parent
4934a51c69
commit
4054a50ce4
18 changed files with 203 additions and 370 deletions
59
README.md
59
README.md
|
@ -20,61 +20,4 @@ func Query(json string, query string) (*model.Node, error)
|
||||||
func QueryArray(json string, query []string) (*model.Node, error)
|
func QueryArray(json string, query []string) (*model.Node, error)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Node methods
|
Other methods: https://pkg.go.dev/go.neonxp.dev/json
|
||||||
|
|
||||||
```go
|
|
||||||
package model // import "go.neonxp.dev/json/model"
|
|
||||||
|
|
||||||
// Node of JSON tree
|
|
||||||
type Node struct {
|
|
||||||
Type NodeType
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNode creates new node from value
|
|
||||||
func NewNode(value any) *Node
|
|
||||||
|
|
||||||
// Get node from object by key
|
|
||||||
func (n *Node) Get(key string) (*Node, error)
|
|
||||||
|
|
||||||
// Index returns node by index from array
|
|
||||||
func (n *Node) Index(idx int) (*Node, error)
|
|
||||||
|
|
||||||
// Set node to object by key
|
|
||||||
func (n *Node) Set(key string, value *Node) error
|
|
||||||
|
|
||||||
// SetIndex sets node to array by index
|
|
||||||
func (n *Node) SetIndex(idx int, value *Node) error
|
|
||||||
|
|
||||||
// SetValue to node
|
|
||||||
func (n *Node) SetValue(value any)
|
|
||||||
|
|
||||||
// Map callback to each key value pair of object
|
|
||||||
func (n *Node) Map(cb func(key string, value *Node) (*Node, error)) error
|
|
||||||
|
|
||||||
// Each applies callback to each element of array
|
|
||||||
func (n *Node) Each(cb func(idx int, value *Node) error) error
|
|
||||||
|
|
||||||
// Query returns node by array query
|
|
||||||
func (n *Node) Query(query []string) (*Node, error)
|
|
||||||
|
|
||||||
// Value returns value of node
|
|
||||||
func (n *Node) Value() any
|
|
||||||
|
|
||||||
// MarshalJSON to []byte
|
|
||||||
func (n *Node) MarshalJSON() ([]byte, error)
|
|
||||||
|
|
||||||
// Merge two object or array nodes
|
|
||||||
func (n *Node) Merge(node *Node) error
|
|
||||||
|
|
||||||
// Len returns length of object or array nodes
|
|
||||||
func (n *Node) Len() (int, error)
|
|
||||||
|
|
||||||
// Compare current node with another node
|
|
||||||
func (n *Node) Compare(op Operand, node *Node) bool
|
|
||||||
|
|
||||||
// Remove by key from object
|
|
||||||
func (n *Node) Remove(key string) error
|
|
||||||
|
|
||||||
// RemoveIndex from array
|
|
||||||
func (n *Node) RemoveIndex(idx int) error
|
|
||||||
```
|
|
12
json.go
12
json.go
|
@ -8,29 +8,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Marshal Node tree to []byte
|
// Marshal Node tree to []byte
|
||||||
func Marshal(node *model.Node) ([]byte, error) {
|
func Marshal(node model.Node) ([]byte, error) {
|
||||||
return node.MarshalJSON()
|
return node.MarshalJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal data to Node tree
|
// Unmarshal data to Node tree
|
||||||
func Unmarshal(data []byte) (*model.Node, error) {
|
func Unmarshal(data []byte) (model.Node, error) {
|
||||||
return parser.Parse(string(data))
|
return parser.Parse(string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query returns node by query string (dot notation)
|
// Query returns node by query string (dot notation)
|
||||||
func Query(json string, query string) (*model.Node, error) {
|
func Query(json string, query string) (model.Node, error) {
|
||||||
n, err := parser.Parse(json)
|
n, err := parser.Parse(json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return n.Query(strings.Split(query, "."))
|
return model.Query(n, strings.Split(query, "."))
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryArray returns node by array query
|
// QueryArray returns node by array query
|
||||||
func QueryArray(json string, query []string) (*model.Node, error) {
|
func QueryArray(json string, query []string) (model.Node, error) {
|
||||||
n, err := parser.Parse(json)
|
n, err := parser.Parse(json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return n.Query(query)
|
return model.Query(n, query)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ func TestQuery(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want *model.Node
|
want model.Node
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// Index returns node by index from array
|
|
||||||
func (n *Node) Index(idx int) (*Node, error) {
|
|
||||||
arrlen := len(n.ArrayValue)
|
|
||||||
if idx >= arrlen {
|
|
||||||
return nil, fmt.Errorf("index %d out of range (len=%d)", idx, arrlen)
|
|
||||||
}
|
|
||||||
return n.ArrayValue[idx], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIndex sets node to array by index
|
|
||||||
func (n *Node) SetIndex(idx int, value *Node) error {
|
|
||||||
arrlen := len(n.ArrayValue)
|
|
||||||
if idx >= arrlen {
|
|
||||||
return fmt.Errorf("index %d out of range (len=%d)", idx, arrlen)
|
|
||||||
}
|
|
||||||
n.ArrayValue[idx] = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Each applies callback to each element of array
|
|
||||||
func (n *Node) Each(cb func(idx int, value *Node) error) error {
|
|
||||||
for i, v := range n.ArrayValue {
|
|
||||||
if err := cb(i, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveIndex from array
|
|
||||||
func (n *Node) RemoveIndex(idx int) error {
|
|
||||||
arrlen := len(n.ArrayValue)
|
|
||||||
if idx >= arrlen {
|
|
||||||
return fmt.Errorf("index %d out of range (len=%d)", idx, arrlen)
|
|
||||||
}
|
|
||||||
n.ArrayValue = append(n.ArrayValue[:idx], n.ArrayValue[idx:]...)
|
|
||||||
return nil
|
|
||||||
}
|
|
48
model/arrayNode.go
Normal file
48
model/arrayNode.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ArrayNode struct {
|
||||||
|
Value NodeArrayValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n ArrayNode) Type() NodeType {
|
||||||
|
return ArrayType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ArrayNode) MarshalJSON() ([]byte, error) {
|
||||||
|
result := make([][]byte, 0, len(n.Value))
|
||||||
|
for _, v := range n.Value {
|
||||||
|
b, err := v.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, b)
|
||||||
|
}
|
||||||
|
return bytes.Join(
|
||||||
|
[][]byte{
|
||||||
|
[]byte("["),
|
||||||
|
bytes.Join(result, []byte(", ")),
|
||||||
|
[]byte("]"),
|
||||||
|
}, []byte("")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ArrayNode) Index(idx int) (Node, error) {
|
||||||
|
if len(n.Value) <= idx {
|
||||||
|
return nil, fmt.Errorf("index %d out of range [0...%d]", idx, len(n.Value)-1)
|
||||||
|
}
|
||||||
|
return n.Value[idx], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ArrayNode) Merge(n2 *ArrayNode) {
|
||||||
|
n.Value = append(n.Value, n2.Value...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ArrayNode) Len() int {
|
||||||
|
return len(n.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeArrayValue []Node
|
16
model/booleanNode.go
Normal file
16
model/booleanNode.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type BooleanNode struct {
|
||||||
|
Value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n BooleanNode) Type() NodeType {
|
||||||
|
return BooleanType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *BooleanNode) MarshalJSON() ([]byte, error) {
|
||||||
|
if n.Value {
|
||||||
|
return []byte("true"), nil
|
||||||
|
}
|
||||||
|
return []byte("false"), nil
|
||||||
|
}
|
|
@ -1,55 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
// Compare current node with another node
|
|
||||||
func (n *Node) Compare(op Operand, node *Node) bool {
|
|
||||||
switch op {
|
|
||||||
case OpEq:
|
|
||||||
return n.Value() == node.Value()
|
|
||||||
case OpNeq:
|
|
||||||
return n.Value() != node.Value()
|
|
||||||
case OpLess:
|
|
||||||
return less(n, node)
|
|
||||||
case OpGt:
|
|
||||||
return less(node, n)
|
|
||||||
case OpLessEq:
|
|
||||||
return less(n, node) || n.Value() == node.Value()
|
|
||||||
case OpGtEq:
|
|
||||||
return less(node, n) || n.Value() == node.Value()
|
|
||||||
case OpIn:
|
|
||||||
if n.Type != ArrayNode {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, v := range n.ArrayValue {
|
|
||||||
if v.Value() == node.Value() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func less(n1 *Node, n2 *Node) bool {
|
|
||||||
if n1.Type != n2.Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch n1.Type {
|
|
||||||
case NumberNode:
|
|
||||||
return n1.NumberValue < n2.NumberValue
|
|
||||||
case StringNode:
|
|
||||||
return n1.StringValue < n2.StringValue
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Operand int
|
|
||||||
|
|
||||||
const (
|
|
||||||
OpEq Operand = iota
|
|
||||||
OpNeq
|
|
||||||
OpLess
|
|
||||||
OpLessEq
|
|
||||||
OpGt
|
|
||||||
OpGtEq
|
|
||||||
OpIn
|
|
||||||
)
|
|
52
model/map.go
52
model/map.go
|
@ -1,52 +0,0 @@
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get node from object by key
|
|
||||||
func (n *Node) Get(key string) (*Node, error) {
|
|
||||||
if n.Type != ObjectNode {
|
|
||||||
return nil, fmt.Errorf("node must be object, got %s", n.Type)
|
|
||||||
}
|
|
||||||
node, ok := n.ObjectValue[key]
|
|
||||||
if !ok {
|
|
||||||
keys := make([]string, 0, len(n.ObjectValue))
|
|
||||||
for k := range n.ObjectValue {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("field '%s' does not exist in object (keys %s)", key, strings.Join(keys, ", "))
|
|
||||||
}
|
|
||||||
return node, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set node to object by key
|
|
||||||
func (n *Node) Set(key string, value Node) error {
|
|
||||||
if n.Type != ObjectNode {
|
|
||||||
return fmt.Errorf("node must be object, got %s", n.Type)
|
|
||||||
}
|
|
||||||
n.ObjectValue[key] = &value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map callback to each key value pair of object
|
|
||||||
func (n *Node) Map(cb func(key string, value *Node) (*Node, error)) error {
|
|
||||||
for k, v := range n.ObjectValue {
|
|
||||||
newNode, err := cb(k, v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
n.ObjectValue[k] = newNode
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove by key from object
|
|
||||||
func (n *Node) Remove(key string) error {
|
|
||||||
if n.Type != ObjectNode {
|
|
||||||
return fmt.Errorf("node must be object, got %s", n.Type)
|
|
||||||
}
|
|
||||||
delete(n.ObjectValue, key)
|
|
||||||
return nil
|
|
||||||
}
|
|
159
model/node.go
159
model/node.go
|
@ -1,154 +1,39 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Node of JSON tree
|
// Node of JSON tree
|
||||||
type Node struct {
|
type Node interface {
|
||||||
Type NodeType
|
Type() NodeType
|
||||||
Meta NodeObjectValue
|
MarshalJSON() ([]byte, error)
|
||||||
StringValue string
|
|
||||||
NumberValue float64
|
|
||||||
ObjectValue NodeObjectValue
|
|
||||||
ArrayValue NodeArrayValue
|
|
||||||
BooleanValue bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNode creates new node from value
|
// NewNode creates new node from value
|
||||||
func NewNode(value any) *Node {
|
func NewNode(value any) Node {
|
||||||
n := new(Node)
|
|
||||||
n.SetValue(value)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns value of node
|
|
||||||
func (n *Node) Value() any {
|
|
||||||
switch n.Type {
|
|
||||||
case StringNode:
|
|
||||||
return n.StringValue
|
|
||||||
case NumberNode:
|
|
||||||
return n.NumberValue
|
|
||||||
case ObjectNode:
|
|
||||||
return n.ObjectValue
|
|
||||||
case ArrayNode:
|
|
||||||
return n.ArrayValue
|
|
||||||
case BooleanNode:
|
|
||||||
return n.BooleanValue
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetValue to node
|
|
||||||
func (n *Node) SetValue(value any) {
|
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
n.Type = StringNode
|
return &StringNode{
|
||||||
n.StringValue = value
|
Value: value,
|
||||||
|
}
|
||||||
case float64:
|
case float64:
|
||||||
n.Type = NumberNode
|
return &NumberNode{
|
||||||
n.NumberValue = value
|
Value: value,
|
||||||
|
}
|
||||||
case int:
|
case int:
|
||||||
n.Type = NumberNode
|
return &NumberNode{
|
||||||
n.NumberValue = float64(value)
|
Value: float64(value),
|
||||||
|
}
|
||||||
case NodeObjectValue:
|
case NodeObjectValue:
|
||||||
n.Type = ObjectNode
|
return &ObjectNode{
|
||||||
meta, hasMeta := value["@"]
|
Value: value,
|
||||||
if hasMeta {
|
|
||||||
n.Meta = meta.ObjectValue
|
|
||||||
delete(value, "@")
|
|
||||||
}
|
}
|
||||||
n.ObjectValue = value
|
|
||||||
case NodeArrayValue:
|
case NodeArrayValue:
|
||||||
n.Type = ArrayNode
|
return &ArrayNode{
|
||||||
n.ArrayValue = value
|
Value: value,
|
||||||
|
}
|
||||||
case bool:
|
case bool:
|
||||||
n.Type = BooleanNode
|
return &BooleanNode{
|
||||||
n.BooleanValue = value
|
Value: value,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
n.Type = NullNode
|
return NullNode{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON to []byte
|
|
||||||
func (n *Node) MarshalJSON() ([]byte, error) {
|
|
||||||
switch n.Type {
|
|
||||||
case StringNode:
|
|
||||||
return []byte(`"` + n.StringValue + `"`), nil
|
|
||||||
case NumberNode:
|
|
||||||
return []byte(strconv.FormatFloat(n.NumberValue, 'g', -1, 64)), nil
|
|
||||||
case ObjectNode:
|
|
||||||
result := make([][]byte, 0, len(n.ObjectValue))
|
|
||||||
for k, v := range n.ObjectValue {
|
|
||||||
b, err := v.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result = append(result, []byte(fmt.Sprintf("\"%s\": %s", k, b)))
|
|
||||||
}
|
|
||||||
return bytes.Join(
|
|
||||||
[][]byte{
|
|
||||||
[]byte("{"),
|
|
||||||
bytes.Join(result, []byte(", ")),
|
|
||||||
[]byte("}"),
|
|
||||||
}, []byte("")), nil
|
|
||||||
case ArrayNode:
|
|
||||||
result := make([][]byte, 0, len(n.ArrayValue))
|
|
||||||
for _, v := range n.ArrayValue {
|
|
||||||
b, err := v.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result = append(result, b)
|
|
||||||
}
|
|
||||||
return bytes.Join(
|
|
||||||
[][]byte{
|
|
||||||
[]byte("["),
|
|
||||||
bytes.Join(result, []byte(", ")),
|
|
||||||
[]byte("]"),
|
|
||||||
}, []byte("")), nil
|
|
||||||
case BooleanNode:
|
|
||||||
if n.BooleanValue {
|
|
||||||
return []byte("true"), nil
|
|
||||||
}
|
|
||||||
return []byte("false"), nil
|
|
||||||
default:
|
|
||||||
return []byte("null"), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge two object or array nodes
|
|
||||||
func (n *Node) Merge(node *Node) error {
|
|
||||||
if n.Type != node.Type {
|
|
||||||
return fmt.Errorf("can't merge nodes of different types")
|
|
||||||
}
|
|
||||||
switch n.Type {
|
|
||||||
case ObjectNode:
|
|
||||||
for k, v := range node.ObjectValue {
|
|
||||||
n.ObjectValue[k] = v
|
|
||||||
}
|
|
||||||
case ArrayNode:
|
|
||||||
n.ArrayValue = append(n.ArrayValue, node.ArrayValue...)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("merge not implemented for type %s", n.Type)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns length of object or array nodes
|
|
||||||
func (n *Node) Len() (int, error) {
|
|
||||||
switch n.Type {
|
|
||||||
case ObjectNode:
|
|
||||||
return len(n.ObjectValue), nil
|
|
||||||
case ArrayNode:
|
|
||||||
return len(n.ArrayValue), nil
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("merge not implemented for type %s", n.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meta represents node metadata
|
|
||||||
type Meta map[string]any
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
func TestNode_MarshalJSON(t *testing.T) {
|
func TestNode_MarshalJSON(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
node *Node
|
node Node
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
11
model/nullNode.go
Normal file
11
model/nullNode.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type NullNode struct{}
|
||||||
|
|
||||||
|
func (n NullNode) Type() NodeType {
|
||||||
|
return NullType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NullNode) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
15
model/numberNode.go
Normal file
15
model/numberNode.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
type NumberNode struct {
|
||||||
|
Value float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NumberNode) Type() NodeType {
|
||||||
|
return NumberType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NumberNode) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(strconv.FormatFloat(n.Value, 'g', -1, 64)), nil
|
||||||
|
}
|
53
model/objectNode.go
Normal file
53
model/objectNode.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ObjectNode struct {
|
||||||
|
Value map[string]Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n ObjectNode) Type() NodeType {
|
||||||
|
return ObjectType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ObjectNode) MarshalJSON() ([]byte, error) {
|
||||||
|
result := make([][]byte, 0, len(n.Value))
|
||||||
|
for k, v := range n.Value {
|
||||||
|
b, err := v.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, []byte(fmt.Sprintf("\"%s\": %s", k, b)))
|
||||||
|
}
|
||||||
|
return bytes.Join(
|
||||||
|
[][]byte{
|
||||||
|
[]byte("{"),
|
||||||
|
bytes.Join(result, []byte(", ")),
|
||||||
|
[]byte("}"),
|
||||||
|
}, []byte("")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ObjectNode) Set(k string, v any) {
|
||||||
|
n.Value[k] = NewNode(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ObjectNode) Get(k string) (Node, error) {
|
||||||
|
child, ok := n.Value[k]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("field %s not found", k)
|
||||||
|
}
|
||||||
|
return child, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ObjectNode) Merge(n2 *ObjectNode) {
|
||||||
|
for k, v := range n2.Value {
|
||||||
|
n.Value[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *ObjectNode) Len() int {
|
||||||
|
return len(n.Value)
|
||||||
|
}
|
|
@ -6,13 +6,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Query returns node by array query
|
// Query returns node by array query
|
||||||
func (n *Node) Query(query []string) (*Node, error) {
|
func Query(n Node, query []string) (Node, error) {
|
||||||
if len(query) == 0 {
|
if len(query) == 0 {
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
head, rest := query[0], query[1:]
|
head, rest := query[0], query[1:]
|
||||||
switch n.Type {
|
switch n := n.(type) {
|
||||||
case ArrayNode:
|
case *ArrayNode:
|
||||||
idx, err := strconv.Atoi(head)
|
idx, err := strconv.Atoi(head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("index must be a number, got %s", head)
|
return nil, fmt.Errorf("index must be a number, got %s", head)
|
||||||
|
@ -21,13 +21,13 @@ func (n *Node) Query(query []string) (*Node, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return next.Query(rest)
|
return Query(next, rest)
|
||||||
case ObjectNode:
|
case *ObjectNode:
|
||||||
next, err := n.Get(head)
|
next, err := n.Get(head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return next.Query(rest)
|
return Query(next, rest)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("can't get %s from node type %s", head, n.Type)
|
return nil, fmt.Errorf("can't get %s from node type %s", head, n.Type())
|
||||||
}
|
}
|
||||||
|
|
13
model/stringNode.go
Normal file
13
model/stringNode.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
type StringNode struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n StringNode) Type() NodeType {
|
||||||
|
return StringType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *StringNode) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(`"` + n.Value + `"`), nil
|
||||||
|
}
|
|
@ -3,18 +3,16 @@ package model
|
||||||
type NodeType string
|
type NodeType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StringNode NodeType = "string"
|
StringType NodeType = "string"
|
||||||
NumberNode NodeType = "number"
|
NumberType NodeType = "number"
|
||||||
ObjectNode NodeType = "object"
|
ObjectType NodeType = "object"
|
||||||
ArrayNode NodeType = "array"
|
ArrayType NodeType = "array"
|
||||||
BooleanNode NodeType = "boolean"
|
BooleanType NodeType = "boolean"
|
||||||
NullNode NodeType = "null"
|
NullType NodeType = "null"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeObjectValue map[string]*Node
|
type NodeObjectValue map[string]Node
|
||||||
|
|
||||||
func (n NodeObjectValue) Set(k string, v any) {
|
func (n NodeObjectValue) Set(k string, v any) {
|
||||||
n[k] = NewNode(v)
|
n[k] = NewNode(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeArrayValue []*Node
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"go.neonxp.dev/json/model"
|
"go.neonxp.dev/json/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Parse(json string) (*model.Node, error) {
|
func Parse(json string) (model.Node, error) {
|
||||||
l := newLexer(json)
|
l := newLexer(json)
|
||||||
go l.Run(initJson)
|
go l.Run(initJson)
|
||||||
n, err := parse(l.Output)
|
n, err := parse(l.Output)
|
||||||
|
|
|
@ -14,7 +14,7 @@ func TestParse(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
want *model.Node
|
want model.Node
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue