Skip to content

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* or std::string_view for simple string literal transformations
  • std::string when 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., XMLParserxml_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:

Point p{3.14, 2.71};
std::string json = glz::write_json(p).value();
// Output: {"X":3.14,"Y":2.71}

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_key must return a type convertible to std::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_key variant per type