Rename Keys¶
The rename_key functionality in Glaze allows you to transform struct member names during JSON serialization and deserialization. This powerful feature operates entirely at compile time, directly affecting compile-time hash maps for optimal runtime performance.
Overview¶
By default, Glaze uses C++ struct member names as JSON keys. However, you may need different key names in your JSON output to:
- Follow specific naming conventions (e.g., camelCase vs snake_case)
- Interface with external APIs that expect particular key formats
- Add prefixes, suffixes, or other transformations to keys
- Maintain backward compatibility while refactoring struct member names
Basic Usage¶
To use rename_key, specialize the glz::meta template for your struct and implement a rename_key function:
struct my_struct_t {
std::string member_name{};
};
template <>
struct glz::meta<my_struct_t> {
static constexpr std::string_view rename_key(const std::string_view key) {
// Transform the key as needed
return transformed_key;
}
};
Example 1: Specific Key Mapping¶
Transform specific member names to follow different naming conventions:
struct renamed_t {
std::string first_name{};
std::string last_name{};
int age{};
};
template <>
struct glz::meta<renamed_t> {
static constexpr std::string_view rename_key(const std::string_view key) {
if (key == "first_name") {
return "firstName";
}
else if (key == "last_name") {
return "lastName";
}
return key; // Return unchanged for other keys
}
};
Usage:
renamed_t obj{};
std::string buffer{};
// Writing JSON
glz::write_json(obj, buffer);
// Output: {"firstName":"","lastName":"","age":0}
// Reading JSON
buffer = R"({"firstName":"Kira","lastName":"Song","age":29})";
glz::read_json(obj, buffer);
// obj.first_name == "Kira"
// obj.last_name == "Song"
// obj.age == 29
Example 2: Dynamic Key Transformation¶
Apply systematic transformations to all keys using compile-time string manipulation:
struct suffixed_keys_t {
std::string first{};
std::string last{};
};
template <>
struct glz::meta<suffixed_keys_t> {
static constexpr std::string rename_key(const auto key) {
return std::string(key) + "_name";
}
};
Usage:
suffixed_keys_t obj{};
std::string buffer{};
// Writing JSON
glz::write_json(obj, buffer);
// Output: {"first_name":"","last_name":""}
// Reading JSON
buffer = R"({"first_name":"Kira","last_name":"Song"})";
glz::read_json(obj, buffer);
// obj.first == "Kira"
// obj.last == "Song"
Compile-Time¶
The rename_key function, even when using std::string operates entirely at compile time. No string allocations or transformations at runtime.
Implementation Details¶
Return Types¶
The rename_key function can return:
const char*orstd::string_viewfor simple string literal transformationsstd::stringwhen dynamic string construction is needed (as shown in the suffix example)
Built-in Key Transformers¶
Glaze provides pre-built key transformers for common naming conventions. These transformers automatically convert between different casing styles at compile time.
Available Transformers¶
Simply inherit from the appropriate transformer in your glz::meta specialization:
struct my_struct {
int user_id = 123;
std::string first_name = "John";
bool is_active = true;
};
// Choose your naming convention:
template <> struct glz::meta<my_struct> : glz::camel_case {}; // {"userId":123,"firstName":"John","isActive":true}
template <> struct glz::meta<my_struct> : glz::pascal_case {}; // {"UserId":123,"FirstName":"John","IsActive":true}
template <> struct glz::meta<my_struct> : glz::snake_case {}; // {"user_id":123,"first_name":"John","is_active":true}
template <> struct glz::meta<my_struct> : glz::screaming_snake_case {}; // {"USER_ID":123,"FIRST_NAME":"John","IS_ACTIVE":true}
template <> struct glz::meta<my_struct> : glz::kebab_case {}; // {"user-id":123,"first-name":"John","is-active":true}
template <> struct glz::meta<my_struct> : glz::screaming_kebab_case {}; // {"USER-ID":123,"FIRST-NAME":"John","IS-ACTIVE":true}
template <> struct glz::meta<my_struct> : glz::lower_case {}; // {"userid":123,"firstname":"John","isactive":true}
template <> struct glz::meta<my_struct> : glz::upper_case {}; // {"USERID":123,"FIRSTNAME":"John","ISACTIVE":true}
Transformer Reference¶
| Transformer | Input Example | Output Example |
|---|---|---|
camel_case |
hello_world |
helloWorld |
pascal_case |
hello_world |
HelloWorld |
snake_case |
helloWorld |
hello_world |
screaming_snake_case |
helloWorld |
HELLO_WORLD |
kebab_case |
hello_world |
hello-world |
screaming_kebab_case |
hello_world |
HELLO-WORLD |
lower_case |
HelloWorld |
helloworld |
upper_case |
HelloWorld |
HELLOWORLD |
Advanced: Direct Function Usage¶
The transformation functions are also available for direct use:
std::string camel = glz::to_camel_case("hello_world"); // "helloWorld"
std::string snake = glz::to_snake_case("XMLParser"); // "xml_parser"
std::string kebab = glz::to_kebab_case("HTTPSConnection"); // "https-connection"
Implementation Notes¶
- All transformers handle acronyms intelligently (e.g.,
XMLParser→xml_parser) - Number handling preserves digits in identifiers
- Transformations are bidirectional - the same transformer works for both reading and writing JSON
- Zero runtime overhead - all transformations occur at compile time via
constexpr
Indexed rename_key: Type-Aware Transformations¶
The indexed rename_key variant provides access to member type information, enabling conditional transformations based on compile-time type checks. This is particularly useful when you want to treat different member types differently.
Overview¶
With indexed rename_key, you can access:
- Member index: The template parameter Index
- Member type: Via glz::member_type_t<T, Index>
- Member name: Via glz::member_nameof<Index, T>
This enables powerful compile-time logic for key transformations.
Example 1: Using Enum Type Names as Keys¶
A common use case is to use an enum's type name as the JSON key instead of the struct member name:
namespace mylib {
enum struct MyEnum { First, Second };
enum struct MyFlag { Yes, No };
}
// Register enums with Glaze
template <>
struct glz::meta<mylib::MyEnum> {
using enum mylib::MyEnum;
static constexpr auto value = enumerate(First, Second);
};
template <>
struct glz::meta<mylib::MyFlag> {
using enum mylib::MyFlag;
static constexpr auto value = enumerate(Yes, No);
};
struct AppContext {
int num{};
mylib::MyEnum e{};
mylib::MyFlag f{};
};
template <>
struct glz::meta<AppContext> {
template <size_t Index>
static constexpr auto rename_key() {
using MemberType = glz::member_type_t<AppContext, Index>;
if constexpr (std::is_enum_v<MemberType>) {
return glz::name_v<MemberType>; // Use enum's type name
} else {
return glz::member_nameof<Index, AppContext>; // Use member name
}
}
};
Usage:
AppContext obj{42, mylib::MyEnum::Second, mylib::MyFlag::Yes};
std::string json = glz::write_json(obj).value();
// Output: {"num":42,"mylib::MyEnum":"Second","mylib::MyFlag":"Yes"}
Example 2: Namespace Stripping¶
You can customize how type names are formatted by manipulating them at compile time:
template <>
struct glz::meta<AppContext> {
template <size_t Index>
static constexpr auto rename_key() {
using MemberType = glz::member_type_t<AppContext, Index>;
if constexpr (std::is_enum_v<MemberType>) {
constexpr auto full_name = glz::name_v<MemberType>;
constexpr size_t pos = full_name.rfind("::");
return (pos == std::string_view::npos)
? full_name
: full_name.substr(pos + 2);
} else {
return glz::member_nameof<Index, AppContext>;
}
}
};
Usage:
AppContext obj{42, mylib::MyEnum::Second, mylib::MyFlag::Yes};
std::string json = glz::write_json(obj).value();
// Output: {"num":42,"MyEnum":"Second","MyFlag":"Yes"}
Example 3: Conditional Transformations by Type¶
Apply different transformations based on member types:
struct MixedTypes {
std::string name{};
int count{};
Status status{}; // enum
};
template <>
struct glz::meta<MixedTypes> {
template <size_t Index>
static constexpr auto rename_key() {
using MemberType = glz::member_type_t<MixedTypes, Index>;
constexpr auto name = glz::member_nameof<Index, MixedTypes>;
if constexpr (std::is_enum_v<MemberType>) {
return glz::name_v<MemberType>; // Use type name for enums
} else if constexpr (std::is_integral_v<MemberType>) {
return name; // Keep original name for integers
} else {
return glz::to_camel_case(name); // CamelCase for other types
}
}
};
Example 4: Generic Key Transformation¶
The indexed variant also works for generic transformations without using type information:
struct Point {
double x{};
double y{};
};
template <>
struct glz::meta<Point> {
template <size_t Index>
static constexpr auto rename_key() {
constexpr auto name = glz::member_nameof<Index, Point>;
if constexpr (name == "x") return "X";
else if constexpr (name == "y") return "Y";
else return name;
}
};
Usage:
Benefits¶
- Type-aware: Make decisions based on member types at compile time
- Generic: Works for any transformation logic
- Zero overhead: All logic executes at compile time
- Composable: Combine multiple transformation strategies
- Natural integration: Works seamlessly with automatic reflection
When to Use Each Variant¶
| Variant | Use When |
|---|---|
rename_key(const std::string_view key) |
You need to transform keys based on their string values |
template <size_t Index> rename_key() |
You need access to member types or index information |
Built-in transformers (e.g., glz::camel_case) |
You want simple naming convention conversions |
Implementation Notes¶
- The indexed
rename_keymust return a type convertible tostd::string_view - All transformation logic is evaluated at compile time
- The indexed variant is mutually exclusive with the string-based variant
- You can only define one
rename_keyvariant per type