JSON
JSON is how most services exchange data. In Cromo, the compiler writes the conversion between a struct and JSON by itself, the first time it is needed: no annotation is required on the struct, no code is generated by a separate tool, and the conversion is as fast as code written by hand.
#From structs to JSON and back
import std.json
struct Address {
string city
string country
}
struct Person {
string name
i64 born
list<string> languages
Address address
}
i32 main() {
Person p
p.name = "Grace"
p.born = 1906
p.languages = ["COBOL", "FLOW-MATIC"]
p.address.city = "New York"
p.address.country = "USA"
string text = json.encode(p)
println("{}", text)
Person back = json.decode<Person>(text)
println("{} was born in {}", back.name, back.born)
ret 0
}{"name":"Grace","born":1906,"languages":["COBOL","FLOW-MATIC"],"address":{"city":"New York","country":"USA"}}
Grace was born in 1906json.encode(value)writes compact JSON;json.pretty(value)writes it on several lines, indented.json.decode<T>(text)reads aT. A member that is missing, ornull, leaves the default value of its field; a member without a field is ignored.- The conversion works for numbers,
bool,string, lists, maps with string keys, fixed arrays, enums and structs whose fields can be converted. A field that cannot, as a function, is a compile error that names it.
#Names and fields to skip
The attribute [[json(...)]] before a field changes its name in JSON, or leaves it out:
import std.json
struct Account {
[[json("user_id")]] i64 id
string name
[[json(skip)]] string password
}
i32 main() {
Account a
a.id = 7
a.name = "Ada"
a.password = "secret"
println("{}", json.encode(a))
ret 0
}{"user_id":7,"name":"Ada"}#Enums
A case without data is written as its name; a case with data as an object whose only member is the case:
import std.json
enum Shape {
Empty
Circle(f64 radius)
[[json("rect")]] Rectangle(f64 width, f64 height)
}
i32 main() {
list<Shape> shapes = [Shape.Empty, Shape.Circle(2.5), Shape.Rectangle(1.0, 2.0)]
string text = json.encode(shapes)
println("{}", text)
list<Shape> back = json.decode<list<Shape>>(text)
println("{:?}", back[2])
ret 0
}["Empty",{"Circle":{"radius":2.5}},{"rect":{"width":1.0,"height":2.0}}]
Rectangle(1.0, 2.0)#Values of any shape
When the shape of the data is not known in advance, json.parse gives a json.Value, an enum with the cases Null, Bool, Int, UInt, Float, Text, Array and Object:
import std.json
i32 main() {
json.Value v = json.parse("{\"items\": [1, 2.5, \"x\", null], \"ok\": true}")
json.Value items = v.get("items")
println("{} items, the first is {}", items.len(), items.at(0).asInt())
println("{} {}", items.at(2).asText(), items.at(3).isNull())
switch (items.at(1)) {
case Int(i64 n) {
println("an integer: {}", n)
}
case Float(f64 f) {
println("a float: {}", f)
}
default {
println("something else")
}
}
json.Value made = json.object()
made.set("language", json.Value.Text("Cromo"))
made.set("version", json.Value.Int(27))
println("{}", json.encode(made))
ret 0
}4 items, the first is 1
x true
a float: 2.5
{"language":"Cromo","version":27}Integers and floating-point numbers are kept apart: an identifier of 64 bits keeps all its digits.
#Errors
A text that is not JSON, or JSON that does not fit the type, is an exception that says where:
import std.json
struct Point {
i64 x
i64 y
}
i32 main() {
try {
Point p = json.decode<Point>("{\"x\": 1, \"y\": \"two\"}")
} catch (Exception e) {
println("{}", e.message())
}
try {
json.Value v = json.parse("{\"x\": 1,, }")
} catch (Exception e) {
println("{}", e.message())
}
ret 0
}cannot decode JSON: at y: expected an integer, found a string
cannot read JSON: line 1, column 9: expected the name of a member, in double quotes#Summary
json.encode,json.prettyandjson.decode<T>convert structs, lists, maps and enums, with code written by the compiler.[[json("name")]]renames a field or a case;[[json(skip)]]leaves a field out.json.parsegives ajson.Valuefor data of any shape.- Errors say where the problem is.