github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/json/README.md (about) 1 # JSON Parser 2 3 The JSON parser is a package that provides functionality for parsing and processing JSON strings. This package accepts JSON strings as byte slices. 4 5 Currently, gno does not [support the `reflect` package](https://docs.gno.land/concepts/effective-gno#reflection-is-never-clear), 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. 6 7 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`. 8 9 This package provides methods for manipulating, searching, and extracting the Node type. 10 11 ## State Machine 12 13 To parse JSON strings, a [finite state machine](https://en.wikipedia.org/wiki/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. 14 15 The image below shows the state transitions of the state machine according to the states and input characters. 16 17 ```mermaid 18 stateDiagram-v2 19 [*] --> __: Start 20 __ --> ST: String 21 __ --> MI: Number 22 __ --> ZE: Zero 23 __ --> IN: Integer 24 __ --> T1: Boolean (true) 25 __ --> F1: Boolean (false) 26 __ --> N1: Null 27 __ --> ec: Empty Object End 28 __ --> cc: Object End 29 __ --> bc: Array End 30 __ --> co: Object Begin 31 __ --> bo: Array Begin 32 __ --> cm: Comma 33 __ --> cl: Colon 34 __ --> OK: Success/End 35 ST --> OK: String Complete 36 MI --> OK: Number Complete 37 ZE --> OK: Zero Complete 38 IN --> OK: Integer Complete 39 T1 --> OK: True Complete 40 F1 --> OK: False Complete 41 N1 --> OK: Null Complete 42 ec --> OK: Empty Object Complete 43 cc --> OK: Object Complete 44 bc --> OK: Array Complete 45 co --> OB: Inside Object 46 bo --> AR: Inside Array 47 cm --> KE: Expecting New Key 48 cm --> VA: Expecting New Value 49 cl --> VA: Expecting Value 50 OB --> ST: String in Object (Key) 51 OB --> ec: Empty Object 52 OB --> cc: End Object 53 AR --> ST: String in Array 54 AR --> bc: End Array 55 KE --> ST: String as Key 56 VA --> ST: String as Value 57 VA --> MI: Number as Value 58 VA --> T1: True as Value 59 VA --> F1: False as Value 60 VA --> N1: Null as Value 61 OK --> [*]: End 62 ``` 63 64 ## Examples 65 66 This package provides parsing functionality along with encoding and decoding functionality. The following examples demonstrate how to use this package. 67 68 ### Decoding 69 70 Decoding (or Unmarshaling) is the functionality that converts an input byte slice JSON string into a `Node` type. 71 72 The converted `Node` type allows you to modify the JSON data or search and extract data that meets specific conditions. 73 74 ```go 75 package main 76 77 import ( 78 "fmt" 79 "gno.land/p/demo/json" 80 "gno.land/p/demo/ufmt" 81 ) 82 83 func main() { 84 node, err := json.Unmarshal([]byte(`{"foo": "var"}`)) 85 if err != nil { 86 ufmt.Errorf("error: %v", err) 87 } 88 89 ufmt.Sprintf("node: %v", node) 90 } 91 ``` 92 93 ### Encoding 94 95 Encoding (or Marshaling) is the functionality that converts JSON data represented as a Node type into a byte slice JSON string. 96 97 > ⚠️ Caution: Converting a large `Node` type into a JSON string may _impact performance_. or might be cause _unexpected behavior_. 98 99 ```go 100 package main 101 102 import ( 103 "fmt" 104 "gno.land/p/demo/json" 105 "gno.land/p/demo/ufmt" 106 ) 107 108 func main() { 109 node := ObjectNode("", map[string]*Node{ 110 "foo": StringNode("foo", "bar"), 111 "baz": NumberNode("baz", 100500), 112 "qux": NullNode("qux"), 113 }) 114 115 b, err := json.Marshal(node) 116 if err != nil { 117 ufmt.Errorf("error: %v", err) 118 } 119 120 ufmt.Sprintf("json: %s", string(b)) 121 } 122 ``` 123 124 ### Searching 125 126 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. 127 128 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. 129 130 Here is an example of finding data with a specific key. For more examples, please refer to the [node.gno](node.gno) file. 131 132 ```go 133 package main 134 135 import ( 136 "fmt" 137 "gno.land/p/demo/json" 138 "gno.land/p/demo/ufmt" 139 ) 140 141 func main() { 142 root, err := Unmarshal([]byte(`{"foo": true, "bar": null}`)) 143 if err != nil { 144 ufmt.Errorf("error: %v", err) 145 } 146 147 value, err := root.GetKey("foo") 148 if err != nil { 149 ufmt.Errorf("error occurred while getting key, %s", err) 150 } 151 152 if value.MustBool() != true { 153 ufmt.Errorf("value is not true") 154 } 155 156 value, err = root.GetKey("bar") 157 if err != nil { 158 t.Errorf("error occurred while getting key, %s", err) 159 } 160 161 _, err = root.GetKey("baz") 162 if err == nil { 163 t.Errorf("key baz is not exist. must be failed") 164 } 165 } 166 ``` 167 168 ## Contributing 169 170 Please submit any issues or pull requests for this package through the GitHub repository at [gnolang/gno](<https://github.com/gnolang/gno>).