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>).