Write ordinary C++
#pragma once
#include <optional>
#include <string>
struct User {
std::string name;
int age = 0;
std::optional<std::string> nickname; // json:",omitempty"
};
CJM v0.5.1
Build-time Metadata Compiler for Modern C++
Write ordinary C++ models without repeating every same-name JSON tag. CJM now defaults managed fields to their exact C++ names, builds stable Metadata IR, and emits C++ integration plus JSON Schema artifacts during your build.
Explicit JSON metadata remains for exceptions:
rename a field, add omitempty, or mark a field ignored.
CJM is not a JSON library, schema-first generator, OpenAPI framework, or runtime validator.
Standard C++ in. Standard C++ out. Developers keep C++ models as the source of truth while CJM handles the repetitive integration code.
enum class Status { Active, Disabled };
struct User {
std::string name;
Status status;
std::optional<std::string> nickname; // json:",omitempty"
std::string display_name; // json:"displayName"
int internal_id; // json:"-"
};
cjm generate \
--input user.hpp \
--output user.cjm.hpp
cjm generate-schema \
--input user.hpp \
--output user.schema.json
Focused early-adopter round
CJM v0.5.1 is ready for early adopters who want build-time JSON integration, default field mapping, and JSON Schema artifacts from ordinary Modern C++ models.
The v0.4 public workflow has been dogfooded in a real downstream CMake
project through FetchContent + cjm_generate.
v0.5.0 added opt-in JSON Schema generation, and v0.5.1 makes
same-name JSON field tags optional.
Try it on a practical model. If something fails or feels awkward, share the smallest header, the build command, and whether schema output was involved. CJM remains a documented practical subset, not production-stable v1.0 and not a full C++ parser.
cjm_generate(...)
generated *.cjm.hpp
optional generated schemas
normal C++ test target
CJM's v0.4 public workflow was validated in ull-md-engine through the downstream CMake path documented on main.
The downstream coverage includes optionals, vectors, fixed-size arrays,
ordered and unordered string-keyed maps, nested generated structs,
enums, ignored fields, omitempty, fixed-width integers,
enum string output, generated model-contract metadata, and generated
to_json / from_json round trips.
Downstream quickstart
CJM v0.5.1 can be tried from a downstream CMake project with
FetchContent. Packaged installation is future work, but the
current release already supports the public cjm_generate workflow,
default field mapping, and opt-in JSON Schema generation.
#pragma once
#include <optional>
#include <string>
struct User {
std::string name;
int age = 0;
std::optional<std::string> nickname; // json:",omitempty"
};
include(FetchContent)
FetchContent_Declare(
cxx_json_codegen
GIT_REPOSITORY https://github.com/cjm-labs/cxx-json-codegen.git
GIT_TAG v0.5.1
)
FetchContent_MakeAvailable(cxx_json_codegen)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE nlohmann_json::nlohmann_json)
cjm_generate(
TARGET app
HEADERS user.hpp
GENERATED_TARGET app_cjm_generated
GENERATE_SCHEMAS
GENERATED_SCHEMAS_VAR app_cjm_schemas
)
Schema generation is opt-in. CJM keeps C++ headers under
generated/cjm and schema artifacts under generated/schemas.
#include "user.hpp"
#include "user.cjm.hpp"
#include <nlohmann/json.hpp>
nlohmann::json json = user;
User round_trip = json.get<User>();
Include the original model header first, then the generated *.cjm.hpp
header. The generated backend code provides ordinary to_json /
from_json integration for nlohmann/json.
json:"displayName" for explicit renames, json:",omitempty" for default-name omission, and json:"-" to ignore a field.cjm generate-schema to emit a JSON Schema artifact from a supported header.GENERATE_SCHEMAS to opt into schema generation from cjm_generate(...).#include dependencies yet.Why CJM exists
Practical schema-shaped models often need repetitive glue for JSON integration, configuration, and metadata-aware workflows.
CJM removes that repetition without replacing C++, adding runtime reflection, or asking teams to adopt a new framework.
Architecture
CJM sits inside the build. The parser/frontend extracts source facts, semantic analysis builds Metadata IR, and backends consume that IR to generate ordinary C++ integration code and JSON Schema artifacts.
v0.5.1 current pipeline
Long-term architecture
CJM turns ordinary C++ declarations into a stable Metadata IR, then emits ordinary C++ code and JSON Schema artifacts for supported models.
CJM v0.5.1 makes the normal model-authoring path less repetitive. Untagged managed fields use exact C++ names, while explicit metadata still handles renames, omitempty, and ignored fields.
json:",omitempty"
json:"-" ignored semantics
duplicate effective-name diagnostics
schema + contract alignment
30/30 tests passed
json:",omitempty"
json:"-" preserved as ignored semantics
duplicate effective JSON names diagnosed
object schemas for generated structs
scalar and string schema mappings
fixed-width integer mappings
std::array<T, N> schema extent
std::map<std::string, T>
std::unordered_map<std::string, T>
std::vector<T>
std::optional<T>
enum and enum class string enums
direct generated structs through $ref and $defs
non-optional supported fields in required
generated model-contract metadata
v0.5.1 verifies normalized field semantics and existing generated artifacts through the same parser/frontend -> semantic analysis -> Metadata IR pipeline.
v0.5.1 does not add a new runtime backend. It keeps nlohmann/json as the compatibility backend and strengthens the shared Metadata IR semantics used by schema and contract outputs.
std::optional<T> for enum, generated struct, or container
default-value metadata
OpenAPI route generation
HTTP endpoint policy
runtime JSON Schema validation
arbitrary JSON values
std::variant / std::any
pointer or polymorphic serialization
custom converters
custom enum string mapping policies
time and datetime schema formats
automatic header discovery
full C++ grammar support at the CJM product level
multiline managed field declarations
private fields
native JSON backend
simdjson, Glaze, or yyjson runtime backends
install/package distribution
Principles
Engineering before magic: CJM should remain early but architecture-driven, useful for practical schema-shaped C++ JSON models, and easy to inspect.
Users write standard C++. Generated output is standard C++. Everything in between is CJM's responsibility.
No hidden runtime, no dynamic reflection engine, and no runtime cost introduced by CJM.
Generated C++ should be readable, inspectable, and debuggable when developers need to look inside.
Adding CJM should feel like enabling another compiler tool inside an existing CMake project.
Long-term vision
v0.5.1 generates nlohmann/json integration and JSON Schema artifacts from normalized Metadata IR. The larger goal is a reusable compiler foundation for modern build-time metadata workflows in C++: simple user experience, production-quality engineering, and zero runtime overhead introduced by CJM.
CJM succeeds when developers forget that code generation is happening. They write standard C++, run their normal build, and everything works.