json/model/node.go

155 lines
3.1 KiB
Go

package model
import (
"bytes"
"fmt"
"strconv"
)
// Node of JSON tree
type Node struct {
Type NodeType
Meta NodeObjectValue
StringValue string
NumberValue float64
ObjectValue NodeObjectValue
ArrayValue NodeArrayValue
BooleanValue bool
}
// NewNode creates new node from value
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) {
case string:
n.Type = StringNode
n.StringValue = value
case float64:
n.Type = NumberNode
n.NumberValue = value
case int:
n.Type = NumberNode
n.NumberValue = float64(value)
case NodeObjectValue:
n.Type = ObjectNode
meta, hasMeta := value["@"]
if hasMeta {
n.Meta = meta.ObjectValue
delete(value, "@")
}
n.ObjectValue = value
case NodeArrayValue:
n.Type = ArrayNode
n.ArrayValue = value
case bool:
n.Type = BooleanNode
n.BooleanValue = value
default:
n.Type = 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