|
DAW JSON Link
|
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.
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:
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.
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:
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.
To write a container of character pointers as strings, map the element explicitly as a nullable string. A nullptr element is written as null.
| 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:
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.
Passing an explicit raw mapping writes the value as-is. The value must already be valid, escaped JSON string content.
| 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}`.
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.