![]() |
simdjson 5.0.1
Ridiculously Fast JSON
|
Sometimes you want to generate JSON string outputs efficiently. The simdjson library provides high-performance low-level facilities. When using these low-level functionalities, you are responsible to define the structure of your JSON document. Our more advanced interface automates the process using C++26 static reflection: you get both high speed and high convenience.
The string_builder class is a low-level utility for constructing JSON strings representing documents. It is optimized for performance, potentially leveraging kernel-specific features like SIMD instructions for tasks such as string escaping. This class supports atomic types (e.g., booleans, numbers, strings) but does not handle composed types directly (like arrays or objects). Note that JSON strings are always encoded as UTF-8.
An string_builder is created with an initial buffer capacity (e.g., 1kB). The memory is reallocated when needed. The efficiency of string_builder stems from its internal use of a resizable array or buffer. When you append data, it adds the characters to this buffer, resizing it only when necessary, typically in a way that minimizes reallocations. This approach contrasts with regular string concatenation, where each operation creates a new string, copying all previous content, leading to quadratic time complexity for repeated concatenations.
It has the following methods to add content to the string:
append(number_type v): Appends a number (including booleans) to the JSON buffer. Booleans are converted to the strings "false" or "true". Numbers are formatted according to the JSON standard, with floating-point numbers using the shortest representation that accurately reflects the value.append(char c): Appends a single character to the JSON buffer.append_null(): Appends the string "null" to the JSON buffer.clear(): Clears the contents of the JSON buffer, resetting the position to 0 while retaining the allocated capacity.escape_and_append(std::string_view input): Appends a string view to the JSON buffer after escaping special characters (e.g., quotes, backslashes) as required by JSON.escape_and_append_with_quotes(std::string_view input) Appends a string view surrounded by double quotes (e.g., "input") to the JSON buffer after escaping special characters. For constant strings, you may also do escape_and_append_with_quotes<"mystring">().escape_and_append_with_quotes(char input): Appends a single character surrounded by double quotes (e.g., "c") to the JSON buffer after escaping it if necessary.append_raw(const char *c): Appends a null-terminated C string directly to the JSON buffer without escaping.append_raw(std::string_view input): Appends a string view directly to the JSON buffer without escaping.append_raw(const char *str, size_t len): Appends a specified number of characters from a C string directly to the JSONappend_key_value(key,value): Appends a key and a value ("json":somevalue)append_key_value<"mykey">(value): Appends a key and a value ("json":somevalue), useful when the key is a compile-time constant (C++20).After writing the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
validate_unicode(): Checks if the content in the JSON buffer is valid UTF-8. Returns: true if the content is valid UTF-8, false otherwise.You might need to do unicode validation if you have strings in your data structures containing malformed UTF-8. Note that we do not automatically call validate_unicode().
Once you are satisfied, you can recover the string as follows:
operator std::string(): Converts the JSON buffer to an std::string. (Might throw if an error occurred.)operator std::string_view(): Converts the JSON buffer to an std::string_view. (Might throw if an error occurred.)view(): Returns a view of the written JSON buffer as a simdjson_result<std::string_view> (C++20).The later method (view()) is recommended. For performance reasons, we expect you to explicitly call validate_unicode() as needed (e.g., prior to calling view()).
The string_builder constructor takes an optional parameter which specifies the initial memory allocation in byte. If you know approximately the size of your JSON output, you can pass this value as a parameter (e.g., simdjson::builder::string_builder sb{1233213}).
The string_builder might throw an exception in case of error when you cast it result to std::string_view. If you wish to avoid exceptions, you can use the following programming pattern:
In all cases, the std::string_view instance depends the corresponding string_builder instance.
If you have C++20, you can simplify the code, as the std::vector<double> is automatically supported. Further, we can pass the keys (which are compile-time constant) as template parameter (for improved performance).
With C++20, you can similarly handle standard containers transparently. For example, you can serialize std::map<std::string,T> types.
You can also serialize std::vector<T> types.
You can also skip the creation for the string_builder instance in such simple cases.
We also have an overload for when you want to reuse the same std::string instance:
We do recommend that you create and reuse the string_builder instance for performance reasons.
You can also add custom serialization functions using a tag_invoke function. For example, the following function will allow you to serialize instances of the type Car.
Static reflection (or compile-time reflection) in C++26 introduces a powerful compile-time mechanism that allows a program to inspect and manipulate its own structure, such as types, variables, functions, and other program elements, during compilation. Unlike runtime reflection in languages like Java or Python, C++26’s static reflection operates entirely at compile time, aligning with C++’s emphasis on zero-overhead abstractions and high performance. It means that you can delegate much of the work to the library.
And then you can append your data structures to a string_builder instance automatically. In most cases, it should work automatically:
This is experimental: the syntax may change slightly in the future.
When using C++26 static reflection for automatic serialization (and deserialization), you can annotate your struct members to rename the corresponding JSON keys or to exclude fields from the JSON representation.
The syntax is:
For example:
Serialization then produces:
The skip annotation also affects deserialization: the field keeps its default value and any corresponding key in the input JSON is ignored.
Other annotations, modelled after the Rust serde library, affect serialization:
Enumerators can be renamed with rename (or rename_all on the enumeration), a structure annotated with transparent is serialized as its single data member, and the members of a member annotated with flatten are written in place of that member. See Customizing (de)serialization with annotations for the complete list, including the annotations that only affect deserialization (alias, default_value, default_from, deny_unknown_fields).
In some instances, you might want to create a string directly from your own data type. You can create a string directly, without an explicit string_builder instance with the simdjson::to_json template function. (Under the hood a string_builder instance may still be created.)
If you know the output size, in bytes, of your JSON string, you may pass it as a second parameter (e.g., simdjson::to_json(c, 31123)).
Sometimes you may want to reuse the same std::string instance. We have an overload for this purpose:
You can then also add a third parameter for the expected output size in bytes.
In some instances, your class might have many fields that you do not want to serialize. You can achieve this result with the simdjson::extract_from template. In the following example, we serialize only the year and price fields on the Car instance.
If prefer a version without exceptions and explicit error handling, you can use the following pattern:
If you want to serialize a value in a custom way, you can do it with a tag_invoke specialization like the following example which will map the year attribute to a string.
In some instances, you may want your JSON to be more readable. For this pupose, we also support the Fractured JSON standard.
The result might be as follows. Columns are laid out in the order in which keys first appear.
The fractured_json_options struct allows you to customize the formatting behavior. It includes the following options:
max_total_line_length (default: 120): Maximum total characters per line. Content exceeding this will be expanded to multiple lines.max_inline_complexity (default: 2): Maximum nesting depth for inline rendering. Elements with complexity exceeding this will be expanded. Complexity 0 = scalar, 1 = flat array/object, 2 = one level of nesting.max_compact_array_complexity (default: 2): Maximum complexity for compact array formatting. Arrays with elements of this complexity or less may have multiple items per line.indent_spaces (default: 4): Number of spaces per indentation level.always_expand_depth (default: -1): Forces elements close to the root to always fully expand, regardless of other settings. -1 = none; 0 = root node only; 1 = root node and its children; etc.enable_table_format (default: true): Enable tabular formatting for arrays of similar objects or arrays. When enabled, the rows of such an array are written one per line with their columns aligned. Rows need not have identical keys: columns are ordered by the first occurrence of each key, and a row missing a key gets blank space in that column. Nested arrays/objects within rows are aligned recursively; if the aligned rows do not fit within max_total_line_length, the deepest levels are left unaligned, and if they still do not fit, the array is expanded normally.max_table_row_complexity (default: 2): Maximum complexity of each row of a table. 0 = rows may only be scalars (a single column); 1 = rows may be flat arrays/objects; higher values allow deeper nesting.enable_compact_multiline (default: true): Enable compact multiline arrays. When enabled, arrays of simple elements may have multiple items per line.min_compact_array_row_items (default: 3): Minimum number of items per line for an array to be formatted as a compact multiline array. If fewer items fit on a line, another layout is used.simple_bracket_padding (default: false): Add space inside brackets for containers that hold only scalar values. When true: { "key": "value" }, when false: {"key": "value"}.nested_bracket_padding (default: true): Add space inside brackets for containers that hold at least one nested array/object. When true: { "a": [1, 2] }, when false: {"a": [1, 2]}.colon_padding (default: true): Add space after colons. When true: "key": "value", when false: "key":"value".comma_padding (default: true): Add space after commas in inline content. When true: [1, 2, 3], when false: [1,2,3].comma_placement (default: before_padding_except_numbers): Placement of commas relative to column padding in table-formatted rows and compact multiline arrays. One of:table_comma_placement::before_padding: commas come right after the value: "b": 1, "c": 2.table_comma_placement::after_padding: commas come after the padding, so they line up in their own column: "b": 1 , "c": 2.table_comma_placement::before_padding_except_numbers: like before_padding, except that number columns use after_padding.number_alignment (default: left): Controls alignment of numbers in table columns or compact multiline arrays. One of number_list_alignment::left or number_list_alignment::right. Numbers are always written exactly as in the input. (FracturedJson defaults to decimal-point alignment, which simdjson does not support.)Compared with the reference FracturedJson implementation, simdjson does not support the options related to comments, blank-line preservation, trailing commas, line-ending style, tab indentation, line prefixes, property-name padding in expanded objects (MaxPropNamePadding, ColonBeforePropNamePadding), or the Decimal and Normalize number alignments.
The min_table_rows and table_similarity_threshold options found in earlier versions of simdjson have been removed.