C++
Five projectors emit C++17/20 struct/enum-class declarations, one per JSON library — same field-mapping conventions (array→std::vector, map→ std::map, optional→std::optional), different serialization glue. Nested object/enum fields hoist to their own top-level named declaration (PascalCased from the field name) rather than nested classes, sidestepping forward-declaration ordering.
nlohmann/json
import { toCpp } from "@rhi-zone/fractal-type-ir/cpp-nlohmann"
toCpp(t(types.object({
id: t(types.string),
name: t(types.string),
email: t(types.string),
age: opt(t(types.integer)),
})), "User")#include <cstdint>
#include <nlohmann/json.hpp>
#include <optional>
#include <string>
struct User {
std::string id;
std::string name;
std::string email;
std::optional<int64_t> age;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(User, id, name, email, age)
};nlohmann's ADL-based adl_serializer handles std::vector/std::optional/ std::map/nested structs generically — the intrusive macro only has to name the fields. unknown degrades to nlohmann::json itself.
RapidJSON
import { toRapidjson } from "@rhi-zone/fractal-type-ir/cpp-rapidjson"
toRapidjson(t(types.object({
street: t(types.string),
city: t(types.string),
})), "Address")RapidJSON has no ADL-based generic dispatch, so every conversion is spelled out by hand against rapidjson::Document/Value:
struct Address {
std::string street;
std::string city;
rapidjson::Value toJson(rapidjson::Document::AllocatorType& allocator) const {
rapidjson::Value v(rapidjson::kObjectType);
v.AddMember("street", rapidjson::Value(street.c_str(), allocator), allocator);
v.AddMember("city", rapidjson::Value(city.c_str(), allocator), allocator);
return v;
}
static Address fromJson(const rapidjson::Value& v) {
Address result;
result.street = std::string(v["street"].GetString(), v["street"].GetStringLength());
result.city = std::string(v["city"].GetString(), v["city"].GetStringLength());
return result;
}
};unknown degrades to rapidjson::Document (not Value) specifically because it owns its own allocator.
simdjson
import { toSimdjson } from "@rhi-zone/fractal-type-ir/cpp-simdjson"
toSimdjson(t(types.object({ id: t(types.string) })), "Item")simdjson's dom API is read-only by design — only fromJson, no toJson counterpart:
#include <simdjson.h>
#include <string>
struct Item {
std::string id;
static Item fromJson(simdjson::dom::element element) {
Item result;
result.id = std::string(element["id"].get_string().value());
return result;
}
};Boost.JSON
import { toBoostJson } from "@rhi-zone/fractal-type-ir/cpp-boost-json"
toBoostJson(t(types.object({ id: t(types.string) })), "Item")Boost.JSON does have ADL-found generic conversion (value_from/value_to recurse through containers on their own), so the generated tag_invoke overloads only name the fields, closer to nlohmann's shape than RapidJSON's:
inline void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, const Item& value) {
jv = { {"id", value.id} };
}
inline Item tag_invoke(const boost::json::value_to_tag<Item>&, const boost::json::value& jv) {
const auto& obj = jv.as_object();
return Item{
boost::json::value_to<std::string>(obj.at("id")),
};
}glaze
import { toGlaze } from "@rhi-zone/fractal-type-ir/cpp-glaze"
toGlaze(t(types.object({ id: t(types.string) })), "Item")glaze reads the struct's shape through a glz::meta<T> specialization — declare the field list once, get both read and write directions for free:
template <>
struct glz::meta<Item> {
using T = Item;
static constexpr auto value = glz::object("id", &Item::id);
};