-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocument.go
141 lines (124 loc) · 2.94 KB
/
document.go
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
// Tideland Go Dynamic JSON
//
// Copyright (C) 2019-2023 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.
package dynaj // import "tideland.dev/go/dynaj"
//--------------------
// IMPORTS
//--------------------
import (
"encoding/json"
"fmt"
)
//--------------------
// DOCUMENT
//--------------------
// Document represents one JSON document.
type Document struct {
root Element
}
// Unmarshal parses the JSON-encoded data and stores the result
// as new document.
func Unmarshal(data []byte) (*Document, error) {
var root any
err := json.Unmarshal(data, &root)
if err != nil {
return nil, fmt.Errorf("cannot unmarshal document: %v", err)
}
return &Document{
root: root,
}, nil
}
// NewDocument creates a new empty document.
func NewDocument() *Document {
return &Document{}
}
// Length returns the number of elements for the given path.
func (d *Document) Length(path Path) int {
node, err := elementAt(d.root, splitPath(path))
if err != nil {
return -1
}
// Return len based on type.
switch n := node.(type) {
case Object:
return len(n)
case Array:
return len(n)
default:
return 1
}
}
// SetValueAt sets the value at the given path.
func (d *Document) SetValueAt(path Path, value Value) error {
keys := splitPath(path)
root, err := insertValue(d.root, keys, value)
if err != nil {
return err
}
d.root = root
return nil
}
// DeleteValueAt deletes the value at the given path. If it is inside
// an object the key is deleted, if it is inside an array the elements
// are shifted.
func (d *Document) DeleteValueAt(path Path) error {
keys := splitPath(path)
root, err := deleteElement(d.root, keys, false)
if err != nil {
return err
}
d.root = root
return nil
}
// DeleteElementAt deletes the element at the given path. It cuts the
// element out of the document tree, regardless if it is a value or
// a container element.
func (d *Document) DeleteElementAt(path Path) error {
keys := splitPath(path)
root, err := deleteElement(d.root, keys, true)
if err != nil {
return err
}
d.root = root
return nil
}
// NodeAt returns the addressed value.
func (d *Document) NodeAt(path Path) *Node {
node := &Node{
path: path,
}
element, err := elementAt(d.root, splitPath(path))
if err != nil {
node.err = fmt.Errorf("invalid path %q: %v", path, err)
} else {
node.element = element
}
return node
}
// Root returns the root path value.
func (d *Document) Root() *Node {
return &Node{
path: Separator,
element: d.root,
}
}
// Clear removes the document data.
func (d *Document) Clear() {
d.root = nil
}
// MarshalJSON implements json.Marshaler.
func (d *Document) MarshalJSON() ([]byte, error) {
return json.Marshal(d.root)
}
// String implements fmt.Stringer.
func (d *Document) String() string {
data, err := json.Marshal(d.root)
if err != nil {
return fmt.Sprintf("cannot marshal document: %v", err)
}
return string(data)
}
// EOF