README.md
JSON Parser
The JSON parser is a package that provides functionality for parsing and processing JSON strings. This package accepts JSON strings as byte slices.
Currently, gno does not support the reflect
package↗, so it cannot retrieve type information at runtime. Therefore, it is designed to infer and handle type information when parsing JSON strings using a state machine approach.
After passing through the state machine, JSON strings are represented as the Node
type. The Node
type represents nodes for JSON data, including various types such as ObjectNode
, ArrayNode
, StringNode
, NumberNode
, BoolNode
, and NullNode
.
This package provides methods for manipulating, searching, and extracting the Node type.
State Machine
To parse JSON strings, a finite state machine↗ approach is used. The state machine transitions to the next state based on the current state and the input character while parsing the JSON string. Through this method, type information can be inferred and processed without reflect, and the amount of parser code can be significantly reduced.
The image below shows the state transitions of the state machine according to the states and input characters.
stateDiagram-v2
[*] --> __: Start
__ --> ST: String
__ --> MI: Number
__ --> ZE: Zero
__ --> IN: Integer
__ --> T1: Boolean (true)
__ --> F1: Boolean (false)
__ --> N1: Null
__ --> ec: Empty Object End
__ --> cc: Object End
__ --> bc: Array End
__ --> co: Object Begin
__ --> bo: Array Begin
__ --> cm: Comma
__ --> cl: Colon
__ --> OK: Success/End
ST --> OK: String Complete
MI --> OK: Number Complete
ZE --> OK: Zero Complete
IN --> OK: Integer Complete
T1 --> OK: True Complete
F1 --> OK: False Complete
N1 --> OK: Null Complete
ec --> OK: Empty Object Complete
cc --> OK: Object Complete
bc --> OK: Array Complete
co --> OB: Inside Object
bo --> AR: Inside Array
cm --> KE: Expecting New Key
cm --> VA: Expecting New Value
cl --> VA: Expecting Value
OB --> ST: String in Object (Key)
OB --> ec: Empty Object
OB --> cc: End Object
AR --> ST: String in Array
AR --> bc: End Array
KE --> ST: String as Key
VA --> ST: String as Value
VA --> MI: Number as Value
VA --> T1: True as Value
VA --> F1: False as Value
VA --> N1: Null as Value
OK --> [*]: End
Examples
This package provides parsing functionality along with encoding and decoding functionality. The following examples demonstrate how to use this package.
Decoding
Decoding (or Unmarshaling) is the functionality that converts an input byte slice JSON string into a Node
type.
The converted Node
type allows you to modify the JSON data or search and extract data that meets specific conditions.
1package main
2
3import (
4 "gno.land/p/demo/json"
5 "gno.land/p/demo/ufmt"
6)
7
8func main() {
9 node, err := json.Unmarshal([]byte(`{"foo": "var"}`))
10 if err != nil {
11 ufmt.Errorf("error: %v", err)
12 }
13
14 ufmt.Sprintf("node: %v", node)
15}
Encoding
Encoding (or Marshaling) is the functionality that converts JSON data represented as a Node type into a byte slice JSON string.
⚠️ Caution: Converting a large
Node
type into a JSON string may impact performance. or might be cause unexpected behavior.
1package main
2
3import (
4 "gno.land/p/demo/json"
5 "gno.land/p/demo/ufmt"
6)
7
8func main() {
9 node := ObjectNode("", map[string]*Node{
10 "foo": StringNode("foo", "bar"),
11 "baz": NumberNode("baz", 100500),
12 "qux": NullNode("qux"),
13 })
14
15 b, err := json.Marshal(node)
16 if err != nil {
17 ufmt.Errorf("error: %v", err)
18 }
19
20 ufmt.Sprintf("json: %s", string(b))
21}
Searching
Once the JSON data converted into a Node
type, you can search and extract data that satisfy specific conditions. For example, you can find data with a specific type or data with a specific key.
To use this functionality, you can use methods in the GetXXX
prefixed methods. The MustXXX
methods also provide the same functionality as the former methods, but they will panic if data doesn't satisfies the condition.
Here is an example of finding data with a specific key. For more examples, please refer to the node.gno file.
1package main
2
3import (
4 "gno.land/p/demo/json"
5 "gno.land/p/demo/ufmt"
6)
7
8func main() {
9 root, err := Unmarshal([]byte(`{"foo": true, "bar": null}`))
10 if err != nil {
11 ufmt.Errorf("error: %v", err)
12 }
13
14 value, err := root.GetKey("foo")
15 if err != nil {
16 ufmt.Errorf("error occurred while getting key, %s", err)
17 }
18
19 if value.MustBool() != true {
20 ufmt.Errorf("value is not true")
21 }
22
23 value, err = root.GetKey("bar")
24 if err != nil {
25 t.Errorf("error occurred while getting key, %s", err)
26 }
27
28 _, err = root.GetKey("baz")
29 if err == nil {
30 t.Errorf("key baz is not exist. must be failed")
31 }
32}
Contributing
Please submit any issues or pull requests for this package through the GitHub repository at gnolang/gno↗.