Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,232 @@
|
||||
> The name `json2` was chosen to avoid any unwanted potential conflicts with the
|
||||
> existing codegen tailored for the main `json` module which is powered by CJSON.
|
||||
|
||||
`x.json2` is an experimental JSON parser written from scratch on V.
|
||||
|
||||
## Usage
|
||||
|
||||
#### encode[T]
|
||||
|
||||
```v
|
||||
import x.json2
|
||||
import time
|
||||
|
||||
struct Person {
|
||||
mut:
|
||||
name string
|
||||
age ?int = 20
|
||||
birthday time.Time
|
||||
deathday ?time.Time
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut person := Person{
|
||||
name: 'Bob'
|
||||
birthday: time.now()
|
||||
}
|
||||
person_json := json2.encode[Person](person)
|
||||
// person_json == {"name": "Bob", "age": 20, "birthday": "2022-03-11T13:54:25.000Z"}
|
||||
}
|
||||
```
|
||||
|
||||
Enums encode as strings by default. Use `@[json_as_number]` on an enum to emit
|
||||
its integer value instead.
|
||||
|
||||
#### decode[T]
|
||||
|
||||
```v
|
||||
import x.json2
|
||||
import time
|
||||
|
||||
struct Person {
|
||||
mut:
|
||||
name string
|
||||
age ?int = 20
|
||||
birthday time.Time
|
||||
deathday ?time.Time
|
||||
}
|
||||
|
||||
fn main() {
|
||||
resp := '{"name": "Bob", "age": 20, "birthday": "${time.now()}"}'
|
||||
person := json2.decode[Person](resp)!
|
||||
// struct Person {
|
||||
// mut:
|
||||
// name "Bob"
|
||||
// age 20
|
||||
// birthday "2022-03-11 13:54:25"
|
||||
// deathday "2022-03-11 13:54:25"
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
decode[T] is smart and can auto-convert the types of struct fields - this means
|
||||
examples below will have the same result
|
||||
|
||||
Embedded struct fields are decoded from the surrounding object, including reference fields.
|
||||
|
||||
```v ignore
|
||||
json2.decode[Person]('{"name": "Bob", "age": 20, "birthday": "2022-03-11T13:54:25.000Z"}')!
|
||||
json2.decode[Person]('{"name": "Bob", "age": 20, "birthday": "2022-03-11 13:54:25.000"}')!
|
||||
json2.decode[Person]('{"name": "Bob", "age": "20", "birthday": 1647006865}')!
|
||||
json2.decode[Person]('{"name": "Bob", "age": "20", "birthday": "1647006865"}}')!
|
||||
```
|
||||
|
||||
#### raw decode
|
||||
|
||||
```v
|
||||
import x.json2
|
||||
import net.http
|
||||
|
||||
fn main() {
|
||||
resp := http.get('https://reqres.in/api/products/1')!
|
||||
|
||||
// This returns an Any type
|
||||
raw_product := json2.decode[json2.Any](resp.body)!
|
||||
}
|
||||
```
|
||||
|
||||
#### iterative token scanning
|
||||
|
||||
`x.json2` now exposes low-level scanners that let you process JSON token by
|
||||
token instead of materializing the whole tree first.
|
||||
|
||||
Use `new_scanner()` for in-memory strings:
|
||||
|
||||
```v
|
||||
import x.json2
|
||||
|
||||
fn main() {
|
||||
mut scanner := json2.new_scanner('{"items":[1,2,3]}')
|
||||
for {
|
||||
token := scanner.next()!
|
||||
if token.is_eof() {
|
||||
break
|
||||
}
|
||||
println('${token.kind}: ${token.literal()}')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `new_reader_scanner()` to stream tokens from a file or any `io.Reader`:
|
||||
|
||||
```v
|
||||
import os
|
||||
import x.json2
|
||||
|
||||
fn main() {
|
||||
mut file := os.open('huge.json')!
|
||||
defer {
|
||||
file.close()
|
||||
}
|
||||
|
||||
mut scanner := json2.new_reader_scanner(reader: file)
|
||||
defer {
|
||||
scanner.free()
|
||||
}
|
||||
|
||||
for {
|
||||
token := scanner.next()!
|
||||
if token.is_eof() {
|
||||
break
|
||||
}
|
||||
if token.kind == .str && token.literal() == 'id' {
|
||||
println('found an id key')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Casting `Any` type / Navigating
|
||||
|
||||
```v
|
||||
import x.json2
|
||||
import net.http
|
||||
|
||||
fn main() {
|
||||
resp := http.get('https://reqres.in/api/products/1')!
|
||||
|
||||
raw_product := json2.decode[json2.Any](resp.body)!
|
||||
|
||||
product := raw_product.as_map()
|
||||
data := product['data'] as map[string]json2.Any
|
||||
|
||||
id := data['id'].int() // 1
|
||||
name := data['name'].str() // cerulean
|
||||
year := data['year'].int() // 2000
|
||||
}
|
||||
```
|
||||
|
||||
#### Constructing an `Any` type
|
||||
|
||||
```v
|
||||
import x.json2
|
||||
|
||||
fn main() {
|
||||
mut me := map[string]json2.Any{}
|
||||
me['name'] = 'Bob'
|
||||
me['age'] = 18
|
||||
|
||||
mut arr := []json2.Any{}
|
||||
arr << 'rock'
|
||||
arr << 'papers'
|
||||
arr << json2.null
|
||||
arr << 12
|
||||
|
||||
me['interests'] = arr
|
||||
|
||||
mut pets := map[string]json2.Any{}
|
||||
pets['Sam'] = 'Maltese Shitzu'
|
||||
me['pets'] = pets
|
||||
|
||||
// Stringify to JSON
|
||||
println(me.str())
|
||||
//{
|
||||
// "name":"Bob",
|
||||
// "age":18,
|
||||
// "interests":["rock","papers","scissors",null,12],
|
||||
// "pets":{"Sam":"Maltese"}
|
||||
//}
|
||||
}
|
||||
```
|
||||
|
||||
### Null Values
|
||||
|
||||
`x.json2` has a separate `Null` type for differentiating an undefined value and a null value.
|
||||
To verify that the field you're accessing is a `Null`, use `[typ] is json2.Null`.
|
||||
|
||||
```v ignore
|
||||
fn (mut p Person) from_json(f json2.Any) {
|
||||
obj := f.as_map()
|
||||
if obj['age'] is json2.Null {
|
||||
// use a default value
|
||||
p.age = 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Casting a value to an incompatible type
|
||||
|
||||
`x.json2` provides methods for turning `Any` types into usable types.
|
||||
The following list shows the possible outputs when casting a value to an incompatible type.
|
||||
|
||||
1. Casting non-array values with `as_array()` will return an array with the value as the content.
|
||||
2. Casting non-map values as map (`as_map()`) will return a map with the value as the content.
|
||||
3. Casting non-string values to string (`str()`) will return the
|
||||
JSON string representation of the value.
|
||||
4. Casting non-numeric values to int/float (`int()`/`i64()`/`f32()`/`f64()`) will return zero.
|
||||
|
||||
## Encoding using string builder instead of []u8
|
||||
|
||||
To be more performant, `json2`, in PR 20052, decided to use buffers directly instead of Writers.
|
||||
If you want to use Writers you can follow the steps below:
|
||||
|
||||
```v ignore
|
||||
mut sb := strings.new_builder(64)
|
||||
mut buffer := []u8{}
|
||||
|
||||
json2.encode_value(<some value to be encoded here>, mut buffer)!
|
||||
|
||||
sb.write(buffer)!
|
||||
|
||||
unsafe { buffer.free() }
|
||||
```
|
||||
Reference in new issue
Block a user