DAW JSON Link
Loading...
Searching...
No Matches
Strings

JSON natively encodes strings. There are two options for mapping them to C++ data structures, fully processed and raw strings. Raw strings only find the end of the string and do not change the underlying values.

Escaped

{
"uris": [
"example.com",
"B\u00FCcher.ch",
"happy.cn",
"happy\u5FEB\u4E50.cn",
"\u5FEB\u4E50.cn",
"happy.\u4E2D\u56FD",
"\u5FEB\u4E50.\u4E2D\u56FD",
"www.\u30CF\u30F3\u30C9\u30DC\u30FC\u30EB\u30B5\u30E0\u30BA.com",
"\uD83E\uDD84.com"
]
}

The above JSON document consists of an array member named "uris" with an element type of string.

The C++ std::vector<std::string> of the above json should be equivalent to:

++
std::vector<string> uris = {
"example.com",
"Bücher.ch",
"happy.cn",
"happy快乐.cn",
"快乐.cn",
"happy.中国",
"快乐.中国",
"www.ハンドボールサムズ.com",
"🦄.com"};

To see a working example using this code, refer to cookbook_escaped_strings1_test.cpp.

Below the mapping of the JSON object with C++ data structures.

++
struct WebData {
std::vector<std::string> uris;
};
namespace daw::json {
template<>
struct json_data_contract<WebData> {
using type = json_member_list<
json_array<"uris", std::string>
>;
static inline auto
to_json_data( WebData const &value ) {
return std::forward_as_tuple( value.uris );
}
};
}
string_writer grows its string with resize_and_overwrite, leaving the new bytes uninitialized,...

Trusted, already-escaped strings

By default, json_string validates UTF-8 and escapes the value during serialization. When an application can guarantee that a value is valid UTF-8 and is already escaped as JSON string content, the EscapeValidUTF8::AssumeValid mapping option writes those bytes directly:

++
struct WebData {
std::string escaped_uri;
};
namespace daw::json {
template<>
struct json_data_contract<WebData> {
using type = json_member_list<
json_string<
"escaped_uri",
std::string,
options::string_opt( options::EscapeValidUTF8::AssumeValid )>
>;
static inline auto to_json_data( WebData const &value ) {
return std::forward_as_tuple( value.escaped_uri );
}
};
}

For example, a value containing the bytes already escaped: \"quoted\\"</tt> is safe for this mapping because its quotation marks already have JSON escape characters. A value containing unescaped quotation marks is not safe and can produce invalid JSON. This option affects serialization only. <tt>from_json</tt> continues to decode and unescape a <tt>json_string</tt> normally. Consequently, a value produced by parsing must not automatically be treated as suitable for <tt>AssumeValid</tt>; the application must establish that it is already escaped before serializing it. The default <tt>EscapeValidUTF8::Validate</tt> behavior should be used whenever that guarantee is unavailable. @section autotoc_md208 Raw strings Raw strings are useful where we don't want to process the strings, we know they will never be escaped, or we do not require processing. A raw string, is also simpler in that it only requires a constructor that requires a pointer and size, like <tt>std::string_view</tt> or <tt>std::string</tt>. <br> @icode{json} { "uris": [ "example.com", "B\u00FCcher.ch", "happy.cn", "happy\u5FEB\u4E50.cn", "\u5FEB\u4E50.cn", "happy.\u4E2D\u56FD", "\u5FEB\u4E50.\u4E2D\u56FD", "www.\u30CF\u30F3\u30C9\u30DC\u30FC\u30EB\u30B5\u30E0\u30BA.com", "\uD83E\uDD84.com" ] } @endicode The above JSON document consists of an array member named <tt>"uris"</tt> with an element type of string. The C++ <tt>std::vector\<std::string\></tt> of the above json should be equivalent to when used as <tt>json_string_raw</tt>: @icode{c} ++ std::vector<std::string> uris = { "example.com", "B\u00FCcher.ch", "happy.cn", "happy\u5FEB\u4E50.cn", "\u5FEB\u4E50.cn", "happy.\u4E2D\u56FD", "\u5FEB\u4E50.\u4E2D\u56FD", "www.\u30CF\u30F3\u30C9\u30DC\u30FC\u30EB\u30B5\u30E0\u30BA.com", "\uD83E\uDD84.com" }; @endicode Contrasting with the [Escaped]("#Raw Strings") example, the difference will be that the array element type is <tt>json_string_raw</tt> @icode{c} ++ struct WebData { std::vector<std::string> uris; }; namespace daw::json { template<> json_data_contract<WebData> { using type = json_member_list< json_array<"uris", json_string_raw_no_name<>> >; static inline auto to_json_data( WebData const & value ) { return std::forward_as_tuple( value.uris ); } }; } @endicode @section autotoc_md209 Serializing strings without a mapping <tt>to_json</tt>, <tt>to_json_array</tt>, and <tt>json_writer</tt> can serialize values that have no <tt>json_data_contract</tt>. Because these values did not come from parsing, string values are always escaped. A <tt>std::string_view</tt> is deduced as a raw string only inside a <tt>json_data_contract</tt>, where it holds the still-escaped text from the parsed document (see @ref "/home/runner/work/daw_json_link/daw_json_link/docs/cookbook/mapping_deduction.md" "Mapping Deduction"). Character types are handled before deduction: * <tt>char *</tt> and <tt>char const *</tt> are nul terminated strings. <tt>nullptr</tt> is written as <tt>null</tt>. * <tt>char[N]</tt> and <tt>char const[N]</tt>, including string literals, are strings of length <tt>N - 1</tt>. * <tt>char</tt> is a one character string. <tt>signed char</tt> and <tt>unsigned char</tt>, including <tt>std::int8_t</tt> and <tt>std::uint8_t</tt>, are distinct types and remain numbers. Given @icode{c} ++ char buffer[] = "Hi""; char const *ptr = "A"da"; char const *null_ptr = nullptr; std::vector<char const *> ptrs{ "A"da", nullptr }; @endicode <table class="markdownTable"> <tr class="markdownTableHead"> <th class="markdownTableHeadNone"> Expression

Output

to_json( "A\"da" )

"A\"da"

to_json( buffer )

"Hi\""

to_json( ptr )

"A\"da"

to_json( null_ptr )

null

to_json( "A\"da"s )

"A\"da"

to_json( "A\"da"sv )

"A\"da"

‘to_json( 'x’ )\ilinebr </td> <td class="markdownTableBodyNone">"x"\ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone">to_json( std::int8_t{ 65 } )\ilinebr </td> <td class="markdownTableBodyNone">65\ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone">to_json( std::uint8_t{ 65 } )\ilinebr </td> <td class="markdownTableBodyNone">65\ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone">to_json( std::optional<std::string_view>( "A\"da" ) )`

`"A\"da"\ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone">to_json( std::vector<std::string_view>{ "A\"da", "B\\b" } )`

`["A\"da","B\\b"]\ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone">to_json( std::map<std::string_view, std::string_view>{ { "na\"me", "A\"da" } } )\ilinebr </td> <td class="markdownTableBodyNone">{"na\"me":"A\"da"}\ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone">to_json( std::tuple<int, std::string_view>{ 1, "A\"da" } )`

`[1,"A\"da"]\ilinebr </td> </tr> <tr class="markdownTableRowEven"> <td class="markdownTableBodyNone">to_json( std::vector<char>{ 'a', 'b' } )\ilinebr </td> <td class="markdownTableBodyNone">["a","b"]\ilinebr </td> </tr> <tr class="markdownTableRowOdd"> <td class="markdownTableBodyNone">to_json( ptrs )\ilinebr </td> <td class="markdownTableBodyNone">[65,null]`

Character pointers are only converted when passed directly. Inside a container they are deduced as a nullable char, so to_json( ptrs ) writes the first character's code.

Containers of character pointers

To write a container of character pointers as strings, map the element explicitly as a nullable string. A nullptr element is written as null.

++
using char_ptr_string = daw::json::json_nullable_no_name<
std::vector<char const *> ptrs{ "A\"da", nullptr };

Expression Output
to_json<char_ptr_strings>( ptrs ) ["A\"da",null]
to_json_array<char_ptr_string>( ptrs ) ["A\"da",null]

The same element mapping works as a member of a json_data_contract. The container type must be given, as it is not the default for the element type:

++
struct Names {
std::vector<char const *> names;
};
namespace daw::json {
template<>
struct json_data_contract<Names> {
static constexpr char const names[] = "names";
using type = json_member_list<
json_array<names, char_ptr_string, std::vector<char const *>>>;
static constexpr auto to_json_data( Names const &value ) {
return std::forward_as_tuple( value.names );
}
};
}

to_json( Names{ ptrs } ) writes {"names":["A\"da",null]}. This mapping is serialization only: from_json<Names> does not compile, as a char const * cannot be constructed from a parsed string.

Alternatively, use a container of std::string_view or write the elements with json_writer::write_array_values.

Already escaped values

Passing an explicit raw mapping writes the value as-is. The value must already be valid, escaped JSON string content.

++
using raw = json_string_raw_no_name<daw::string_view>;

Expression Output
to_json<raw>( R"(A\"da)"sv ) "A\"da"
to_json<raw>( R"(A\"da)" ) "A\"da"

A std::string_view member of a json_data_contract is also raw. Given a contract mapping json_link<"value", std::string_view> for Member, to_json( Member{ R"(A\"da)" } ) writes {"value":"A\"da"}. Likewise a char member of a contract, json_link<"c", char>, remains a number: ‘to_json( CharMember{ 'z’ } )writes{"c":122}`.

Invalid UTF-8

With the default RestrictedStringOutput::ErrorInvalidUTF8 output option, valid UTF-8 is written unchanged and invalid UTF-8 is an error.

Expression Output
to_json( "caf\xC3\xA9"sv ) "café"
to_json( "\xC3"sv ) throws json_exception with ErrorReason::InvalidUTFCodepoint

A lone byte above 0x7F is not a complete UTF-8 character, so to_json( char( 0xC3 ) ) also throws.

See Arrays for to_json_array and Incremental JSON Writer for json_writer.