1- # Processors
1+ # Processors
22
33Processors can be used to apply transformations to struct serialization and deserialization.
44
@@ -16,10 +16,10 @@ const auto homer =
1616 .last_name = "Simpson",
1717 .age = 45};
1818
19- const auto json_string =
19+ const auto json_string =
2020 rfl::json::write< rfl::SnakeCaseToCamelCase > (homer);
2121
22- const auto homer2 =
22+ const auto homer2 =
2323 rfl::json::read<Person, rfl::SnakeCaseToCamelCase>(json_string).value();
2424```
2525
@@ -33,27 +33,28 @@ The resulting JSON string looks like this:
3333
3434reflect-cpp currently supports the following processors:
3535
36- - ` rfl::AddStructName `
37- - ` rfl::AddTagsToVariants `
38- - ` rfl::AddNamespacedTagsToVariants `
39- - ` rfl::AllowRawPtrs `
40- - ` rfl::DefaultIfMissing `
41- - ` rfl::NoExtraFields `
42- - ` rfl::NoFieldNames `
43- - ` rfl::NoOptionals `
44- - ` rfl::UnderlyingEnums `
45- - ` rfl::SnakeCaseToCamelCase `
46- - ` rfl::SnakeCaseToPascalCase `
47-
48- ### ` rfl::AddStructName `
36+ - ` rfl::AddStructName `
37+ - ` rfl::AddTagsToVariants `
38+ - ` rfl::AddNamespacedTagsToVariants `
39+ - ` rfl::AllowRawPtrs `
40+ - ` rfl::DefaultIfMissing `
41+ - ` rfl::EnumNamesOnly `
42+ - ` rfl::NoExtraFields `
43+ - ` rfl::NoFieldNames `
44+ - ` rfl::NoOptionals `
45+ - ` rfl::UnderlyingEnums `
46+ - ` rfl::SnakeCaseToCamelCase `
47+ - ` rfl::SnakeCaseToPascalCase `
48+
49+ ### ` rfl::AddStructName `
4950
5051It is also possible to add the struct name as an additional field, like this:
5152
5253``` cpp
53- const auto json_string =
54+ const auto json_string =
5455 rfl::json::write<rfl::AddStructName<" type" >>(homer);
5556
56- const auto homer2 =
57+ const auto homer2 =
5758 rfl::json::read<Person, rfl::AddStructName<" type" >>(json_string).value();
5859```
5960
@@ -63,7 +64,7 @@ The resulting JSON string looks like this:
6364{"type" :" Person" ,"first_name" :" Homer" ,"last_name" :" Simpson" ,"age" :45 }
6465```
6566
66- ### ` rfl::AddTagsToVariants `
67+ ### ` rfl::AddTagsToVariants `
6768
6869This processor automatically adds tags to variants. Consider the following example:
6970
@@ -150,7 +151,7 @@ const auto msgs = std::vector<Messages>{
150151 Error::Message{.error = "failure", .error_id = 404}
151152};
152153
153- // This would cause problems with rfl::AddTagsToVariants because both
154+ // This would cause problems with rfl::AddTagsToVariants because both
154155// structs have the same name "Message"
155156
156157// But this works perfectly:
@@ -203,12 +204,12 @@ This generates:
203204
204205### ` rfl::AllowRawPtrs `
205206
206- By default, reflect-cpp does not allow * reading into* raw pointers, ` std::string_view ` or ` std::span ` .
207- (*Writing from* raw pointers is never a problem.) This is because reading into raw pointers
207+ By default, reflect-cpp does not allow * reading into* raw pointers, ` std::string_view ` or ` std::span ` .
208+ (*Writing from* raw pointers is never a problem.) This is because reading into raw pointers
208209means that the library will allocate memory that the user then has to manually delete . This can lead to misunderstandings and memory leaks.
209210
210- You might want to consider using some alternatives, such as `std::unique_ptr`, `rfl::Box`,
211- `std::shared_ptr`, `rfl::Ref` or `std::optional`.
211+ You might want to consider using some alternatives, such as `std::unique_ptr`, `rfl::Box`,
212+ `std::shared_ptr`, `rfl::Ref` or `std::optional`.
212213But if you absolutely have to use raw pointers, you can pass `rfl::AllowRawPtrs` to `read`:
213214
214215```cpp
@@ -253,7 +254,7 @@ if(!person.span.empty()) {
253254
254255The ` rfl::DefaultIfMissing ` processor is only relevant for reading data. For writing data, it will make no difference.
255256
256- Usually, when fields are missing in the input data, this will lead to an error
257+ Usually, when fields are missing in the input data, this will lead to an error
257258(unless they are optional fields).
258259But if you pass the `rfl::DefaultIfMissing` processor, then missing fields will be
259260replaced by their default value.
@@ -289,13 +290,44 @@ have gotten had you read the following JSON string:
289290Because you have not passed a default value to town, the default value
290291of the type is used instead.
291292
293+ ### ` rfl::EnumNamesOnly `
294+
295+ By default, when reading an enum from a string, numeric values are accepted
296+ in addition to the declared enumerator names, even when they do not
297+ correspond to a declared enumerator. For instance, given this enum:
298+
299+ ``` cpp
300+ enum class Color { red = 1, green = 2, blue = 3 };
301+ ```
302+
303+ reading `{"color":"2"}` will produce `Color::green`, and even
304+ `{"color":"4"}` will succeed and produce the cast value `4`, although `4`
305+ is not a declared enumerator.
306+
307+ If you want to reject numeric values and only accept the declared
308+ enumerator names, pass the `rfl::EnumNamesOnly` processor to `read`:
309+
310+ ```cpp
311+ const auto circle =
312+ rfl::json::read<Circle, rfl::EnumNamesOnly>(json_str);
313+ ```
314+
315+ Now, ` {"color":"2"} ` will lead to an error:
316+
317+ ```
318+ Failed to parse field 'color': Invalid enum value: '2'. Must be one of [red, green, blue].
319+ ```
320+
321+ This processor only affects reading. Enum values that cannot be matched to
322+ a declared name are still written as their integer representation.
323+
292324### ` rfl::NoExtraFields `
293325
294- When reading an object and the object contains a field that cannot be
326+ When reading an object and the object contains a field that cannot be
295327matched to any of the fields in the struct, that field is simply ignored.
296328
297329However, when ` rfl::NoExtraFields ` is added to ` read ` , then such extra fields
298- will lead to an error.
330+ will lead to an error.
299331
300332This can be overriden by adding ` rfl::ExtraFields ` to the struct.
301333
@@ -312,7 +344,7 @@ struct Person {
312344{"first_name":"Homer","last_name":"Simpson","extra_field":0}
313345```
314346
315- If you call ` rfl::json::read<Person>(json_string) ` , then ` extra_field ` will
347+ If you call ` rfl::json::read<Person>(json_string) ` , then ` extra_field ` will
316348simply be ignored.
317349
318350But if you call ` rfl::json::read<Person, rfl::NoExtraFields>(json_string) ` ,
@@ -333,13 +365,13 @@ will not fail, because `extra_field` would be included in `extras`.
333365
334366### `rfl::NoFieldNames`
335367
336- We can also remove the field names altogether:
368+ We can also remove the field names altogether:
337369
338370```cpp
339- const auto json_string =
371+ const auto json_string =
340372 rfl::json::write<rfl::NoFieldNames>(homer);
341373
342- const auto homer2 =
374+ const auto homer2 =
343375 rfl::json::read<Person, rfl::NoFieldNames>(json_string).value();
344376```
345377
@@ -351,19 +383,19 @@ The resulting JSON string looks like this:
351383
352384This is particularly relevant for binary formats, which do not emphasize readability,
353385like msgpack or flexbuffers. Removing the field names can reduce the size of the
354- resulting bytestrings and significantly speed up read and write time,
386+ resulting bytestrings and significantly speed up read and write time,
355387depending on the dataset.
356388
357389However, it makes it more difficult to maintain backwards compatability.
358390
359391Note that ` rfl::NoFieldNames ` is not supported for BSON, TOML, XML, or YAML, due
360- to limitations of these formats.
392+ to limitations of these formats.
361393
362394### ` rfl::NoOptionals `
363395
364396As we have seen in the section on optional fields, when a ` std::optional ` is
365397` std::nullopt ` , it is usually not written at all. But if you want them to be explicitly
366- written as ` null ` , you can use this processor. The same thing applies to ` std::shared_ptr ` and
398+ written as ` null ` , you can use this processor. The same thing applies to ` std::shared_ptr ` and
367399` std::unique_ptr ` .
368400
369401``` cpp
@@ -384,7 +416,7 @@ The resulting JSON string looks like this:
384416{"first_name":"Homer","last_name":"Simpson","town":null}
385417```
386418
387- By default, ` rfl::json::read ` will accept both ` "town":null ` and just
419+ By default, ` rfl::json::read ` will accept both ` "town":null ` and just
388420leaving out the field ` town ` . However, if you want to require the field
389421` town ` to be included, you can add ` rfl::NoOptionals ` to ` read ` :
390422
@@ -424,10 +456,10 @@ Please refer to the example above.
424456If you want ` PascalCase ` instead of ` camelCase ` , you can use the appropriate processor:
425457
426458``` cpp
427- const auto json_string =
459+ const auto json_string =
428460 rfl::json::write<rfl::SnakeCaseToPascalCase>(homer);
429461
430- const auto homer2 =
462+ const auto homer2 =
431463 rfl::json::read<Person, rfl::SnakeCaseToPascalCase>(json_string).value();
432464```
433465
@@ -442,10 +474,10 @@ The resulting JSON string looks like this:
442474You can combine several processors:
443475
444476``` cpp
445- const auto json_string =
477+ const auto json_string =
446478 rfl::json::write<rfl::SnakeCaseToCamelCase, rfl::AddStructName<" type" >>(homer);
447479
448- const auto homer2 =
480+ const auto homer2 =
449481 rfl::json::read<Person, rfl::SnakeCaseToCamelCase, rfl::AddStructName<" type" >>(json_string).value();
450482```
451483
0 commit comments