Custom types and JSON#
AJson is a dynamic tree. Most of the time you'd rather work with your own structures, so aui::json converts them
for you:
aui::to_json(value)converts a C++ value to AJson;aui::from_json<T>(json)converts AJson to a C++ value of typeT. There is also an overloadaui::from_json(json, dst)which writes into an existing object.
Both functions rely on the AJsonConv<T> trait. It is implemented for:
- numbers (
int,int64_t,float,double),bool, AString, APath, AColor; - containers (everything that is a
ranges::range, e.g., AVector) as JSON arrays; AMap<AString, T>as JSON objects;std::pairas a two-element array;AOptional<T>,_<T>,std::shared_ptr<T>(nullfor an emptyAOptional);- enums, by name;
- properties (AProperty), by their underlying value;
- your structures, once you describe their fields with
AJSON_FIELDS; - anything else, once you specialize
AJsonConv.
The conversion nests, so AVector<AMap<AString, int>> works out of the box.
Structures#
AJSON_FIELDS describes how fields of a structure map to JSON keys. Place it in the global namespace, after the
structure definition.
|
Now the structure converts in both directions:
|
If the field name is the same as the key, AJSON_FIELDS_ENTRY(name) saves some typing.
Optional fields#
By default, a missing key makes aui::from_json throw AJsonException. Pass AJsonFieldFlags::OPTIONAL to allow the
key to be absent.
|
When the key is missing, the field keeps its previous value, so use aui::from_json(json, dst) with a pre-filled
dst to provide defaults:
Nested structures#
Fields may be of any type that is convertible, including other structures (with their own AJSON_FIELDS) and
containers of them. See the example in Custom converters.
Enums#
Enums are converted by name. Declare them with AUI_ENUM_VALUES, which also makes the names available to AEnumerate.
|
Custom converters#
When a type needs a representation of its own, specialize AJsonConv<T> with two static functions: toJson and
fromJson.
Say we want Point to look like [x, y] instead of {"x":..., "y":...}:
Once specialized, Point can be used as a field or as an element of a container, just like built-in types:
|
In fromJson, use the as* accessors of AJson; they throw AJsonTypeMismatchException when the JSON has an
unexpected shape, which is what you want.
Error messages#
Exceptions thrown during a conversion carry the location of the faulty value, so you don't have to guess which of the 351 messages is broken. Given these structures:
|
the following code fails with a message that mentions messages[0].content:
|
The path is collected automatically for AJSON_FIELDS, containers and maps. A custom AJsonConv that delegates to
aui::from_json for its parts benefits from it as well.