aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/vlib/x/json2/README.md
blob: fcefbff7cb10d9c64c64ba732f0a4ce32b2d86ea (plain)
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
> 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
```v oksyntax
import x.json2
import net.http

fn main() {
	// Decoding
	resp := http.get('https://example.com') ?

	// raw decode
	raw_person := json2.raw_decode(resp.text) ?

	// Casting `Any` type / Navigating
	person := raw_person.as_map()
	name := person['name'].str() // Bob
	age := person['age'].int() // 19
	pi := person['pi'].f64() // 3.14....

	// Constructing an `Any` type
	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"}
	//}

	// Encode a struct/type to JSON
	encoded_json := json2.encode<Person>(person2)
}
```
## Using `decode<T>` and `encode<T>`
> Codegen for this feature is still WIP.
> You need to manually define the methods before using the module to structs.

In order to use the `decode<T>` and `encode<T>` function, you need to explicitly define
two methods: `from_json` and `to_json`. `from_json` accepts a `json2.Any` argument
and inside of it you need to map the fields you're going to put into the type.
As for `to_json` method, you just need to map the values into `json2.Any`
and turn it into a string.

```v ignore
struct Person {
mut:
    name string
    age  int = 20
    pets []string
}

fn (mut p Person) from_json(f json2.Any) {
    obj := f.as_map()
    for k, v in obj {
        match k {
            'name' { p.name = v.str() }
            'age' { p.age = v.int() }
            'pets' { p.pets = v.arr().map(it.str()) }
            else {}
        }
    }
}

fn (p Person) to_json() string {
    mut obj := map[string]json2.Any
    obj['name'] = p.name
    obj['age'] = p.age
    obj['pets'] = p.pets
    return obj.str()
}

fn main() {
    resp := os.read_file('./person.json')?
    person := json2.decode<Person>(resp)?
    println(person) // Person{name: 'Bob', age: 28, pets: ['Floof']}
    person_json := json2.encode<Person>(person)
    println(person_json) // {"name": "Bob", "age": 28, "pets": ["Floof"]}
}
```

## Using struct tags
`x.json2` can access and use the struct field tags similar to the
`json` module by using the comp-time `$for` for structs.

```v ignore
fn (mut p Person) from_json(f json2.Any) {
    mp := an.as_map()
	mut js_field_name := ''
    $for field in Person.fields {
        js_field_name = field.name

        for attr in field.attrs {
			if attr.starts_with('json:') {
				js_field_name = attr.all_after('json:').trim_left(' ')
				break
			}
		}

        match field.name {
            'name' { p.name = mp[js_field_name].str() }
			'age' { u.age = mp[js_field_name].int() }
			'pets' { u.pets = mp[js_field_name].arr().map(it.str()) }
			else {}
		}
    }
}
```

### 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
    }
}
```

### Custom field names
Aside from using struct tags, you can also just simply cast the base field into a map (`as_map()`)
and access the field you wish to put into the struct/type.

```v ignore
fn (mut p Person) from_json(f json2.Any) {
    obj := f.as_map()
    p.name = obj['nickname'].str()
}
```

```v oksyntax
fn (mut p Person) to_json() string {
	obj := f.as_map()
	obj['nickname'] = p.name
	return obj.str()
}
```

### Undefined Values
Getting undefined values has the same behavior as regular V types.
If you're casting a base field into `map[string]json2.Any` and fetch an undefined entry/value,
it simply returns empty. As for the `[]json2.Any`, it returns an index error.

## 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 as array (`arr()`) 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.