The name
json2
was chosen to avoid any unwanted potential conflicts with the existing codegen tailored for the mainjson
module which is powered by CJSON.
x.json2
is an experimental JSON parser written from scratch on V.
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)
}
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.
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"]}
}
x.json2
can access and use the struct field tags similar to the
json
module by using the comp-time $for
for structs.
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 {}
}
}
}
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
.
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
}
}
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.
fn (mut p Person) from_json(f json2.Any) {
obj := f.as_map()
p.name = obj['nickname'].str()
}
fn (mut p Person) to_json() string {
obj := f.as_map()
obj['nickname'] = p.name
return obj.str()
}
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.
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.
arr()
) will return an array with the value as the content.as_map()
) will return a map with the value as the content.str()
) will return the
JSON string representation of the value.int()
/i64()
/f32()
/f64()
) will return zero.const (
null = Null{}
)
fn decode<T>(src string) ?T
decode is a generic function that decodes a JSON string into the target type.
fn encode<T>(typ T) string
encode is a generic function that encodes a type into a JSON string.
fn fast_raw_decode(src string) ?Any
Same with raw_decode
, but skips the type conversion for certain types when decoding a certain value.
fn raw_decode(src string) ?Any
Decodes a JSON string into an Any
type. Returns an option.
interface Serializable {
from_json(f Any)
to_json() string
}
fn (flds []Any) str() string
str returns the string representation of the []Any
.
type Any = Null | []Any | bool | f32 | f64 | i64 | int | map[string]Any | string
Any
is a sum type that lists the possible types to be decoded and used.
fn (f Any) arr() []Any
arr uses Any
as an array.
fn (f Any) as_map() map[string]Any
as_map uses Any
as a map.
fn (f Any) bool() bool
bool uses Any
as a bool
fn (f Any) f32() f32
f32 uses Any
as a 32-bit float.
fn (f Any) f64() f64
f64 uses Any
as a float.
fn (f Any) i64() i64
i64 uses Any
as a 64-bit integer.
fn (f Any) int() int
int uses Any
as an integer.
fn (f Any) str() string
str returns the string representation of the Any
type.
fn (flds map[string]Any) str() string
str returns the string representation of the map[string]Any
.
struct Null {
}
Null
struct is a simple representation of the null
value in JSON.