x.json2 #
The name
json2was chosen to avoid any unwanted potential conflicts with the existing codegen tailored for the mainjsonmodule which is powered by CJSON.
x.json2 is an experimental JSON parser written from scratch on V.
Usage
import x.json2
import net.http
fn main() {
// Decoding
resp := http.get('https://example.com')!
// raw decode
raw_person := json2.raw_decode(resp.body)!
// 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.
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.
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.
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.
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()
}
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.
- Casting non-array values as array (
arr()) will return an array with the value as the content. - Casting non-map values as map (
as_map()) will return a map with the value as the content. - Casting non-string values to string (
str()) will return the JSON string representation of the value. - Casting non-numeric values to int/float (
int()/i64()/f32()/f64()) will return zero.
Constants #
const (
null = Null{}
)
fn decode #
fn decode<T>(src string) !T
decode is a generic function that decodes a JSON string into the target type.
fn encode #
fn encode<T>(typ T) string
encode is a generic function that encodes a type into a JSON string.
fn fast_raw_decode #
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 #
fn raw_decode(src string) !Any
Decodes a JSON string into an Any type. Returns an option.
interface Serializable #
interface Serializable {
from_json(f Any)
to_json() string
}
fn ([]Any) str #
fn (f []Any) str() string
str returns the JSON string representation of the []Any type.
type Any #
type Any = Null | []Any | bool | f32 | f64 | i64 | int | map[string]Any | string | u64
Any is a sum type that lists the possible types to be decoded and used.
fn (Any) arr #
fn (f Any) arr() []Any
arr uses Any as an array.
fn (Any) as_map #
fn (f Any) as_map() map[string]Any
as_map uses Any as a map.
fn (Any) bool #
fn (f Any) bool() bool
bool uses Any as a bool
fn (Any) f32 #
fn (f Any) f32() f32
f32 uses Any as a 32-bit float.
fn (Any) f64 #
fn (f Any) f64() f64
f64 uses Any as a float.
fn (Any) i64 #
fn (f Any) i64() i64
i64 uses Any as a 64-bit integer.
fn (Any) int #
fn (f Any) int() int
int uses Any as an integer.
fn (Any) json_str #
fn (f Any) json_str() string
json_str returns the JSON string representation of the Any type.
fn (Any) prettify_json_str #
fn (f Any) prettify_json_str() string
prettify_json_str returns the pretty-formatted JSON string representation of the Any type.
fn (Any) str #
fn (f Any) str() string
str returns the string representation of the Any type. Use the json_str method
if you want to use the escaped str() version of the Any type.
fn (Any) u64 #
fn (f Any) u64() u64
u64 uses Any as a 64-bit unsigned integer.
fn (map[string]Any) str #
fn (f map[string]Any) str() string
str returns the JSON string representation of the map[string]Any type.
enum ValueKind #
enum ValueKind {
unknown
array
object
string_
number
}
fn (ValueKind) str #
fn (k ValueKind) str() string
str returns the string representation of the specific ValueKind
struct DecodeError #
struct DecodeError {
line int
column int
message string
}
fn (DecodeError) code #
fn (err DecodeError) code() int
code returns the error code of DecodeError
fn (DecodeError) msg #
fn (err DecodeError) msg() string
msg returns the message of the DecodeError
struct Encoder #
struct Encoder {
newline u8
newline_spaces_count int
escape_unicode bool = true
}
Encoder encodes the an Any type into JSON representation.
It provides parameters in order to change the end result.
fn (Encoder) encode_value #
fn (e &Encoder) encode_value(f Any, mut wr io.Writer) !
encode_value encodes an Any value to the specific writer.
struct InvalidTokenError #
struct InvalidTokenError {
DecodeError
token Token
expected TokenKind
}
fn (InvalidTokenError) code #
fn (err InvalidTokenError) code() int
code returns the error code of the InvalidTokenError
fn (InvalidTokenError) msg #
fn (err InvalidTokenError) msg() string
msg returns the message of the InvalidTokenError
struct Null #
struct Null {
is_null bool = true
}
Null struct is a simple representation of the null value in JSON.
struct Token #
struct Token {
lit []u8
kind TokenKind
line int
col int
}
fn (Token) full_col #
fn (t Token) full_col() int
full_col returns the full column information which includes the length
struct UnknownTokenError #
struct UnknownTokenError {
DecodeError
token Token
kind ValueKind = .unknown
}
fn (UnknownTokenError) code #
fn (err UnknownTokenError) code() int
code returns the error code of the UnknownTokenError
fn (UnknownTokenError) msg #
fn (err UnknownTokenError) msg() string
msg returns the error message of the UnknownTokenError