├── .cmake-format.py ├── .github └── workflows │ └── ccpp.yaml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── _clang-format ├── autojsoncxx ├── autojsoncxx.hpp ├── autojsoncxx.py ├── examples │ ├── failure │ │ ├── duplicate_key.json │ │ ├── duplicate_key_user.json │ │ ├── hard.json │ │ ├── integer_string.json │ │ ├── map_element_mismatch.json │ │ ├── missing_required.json │ │ ├── null_in_key.json │ │ ├── out_of_range.json │ │ ├── single_object.json │ │ └── unknown_field.json │ └── success │ │ ├── hard.json │ │ ├── user_array.json │ │ ├── user_array_compact.json │ │ └── user_map.json ├── userdef.hpp └── userdef.json ├── cmake └── staticjson-config.cmake.in ├── examples ├── failure │ ├── duplicate_key.json │ ├── duplicate_key_user.json │ ├── hard.json │ ├── integer_string.json │ ├── invalid_enum.json │ ├── map_element_mismatch.json │ ├── missing_required.json │ ├── null_in_key.json │ ├── out_of_range.json │ ├── single_object.json │ ├── tensor_length_error.json │ ├── tensor_type_mismatch.json │ └── unknown_field.json └── success │ ├── hard.json │ ├── tensor.json │ ├── user_array.json │ ├── user_array_compact.json │ └── user_map.json ├── format.sh ├── include └── staticjson │ ├── basic.hpp │ ├── document.hpp │ ├── enum.hpp │ ├── error.hpp │ ├── forward_declarations.hpp │ ├── io.hpp │ ├── optional_support.hpp │ ├── primitive_types.hpp │ ├── staticjson.hpp │ └── stl_types.hpp ├── src └── staticjson.cpp └── test ├── myarray.hpp ├── test_autojsoncxx.cpp ├── test_basic.cpp ├── test_example.cpp ├── test_instantiation.cpp ├── test_integration.cpp ├── test_memory_usage.cpp └── test_tensor.cpp /.cmake-format.py: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # Options affecting listfile parsing 3 | # ---------------------------------- 4 | with section("parse"): 5 | 6 | # Specify structure for custom cmake functions 7 | additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'], 8 | 'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}} 9 | 10 | # Override configurations per-command where available 11 | override_spec = {} 12 | 13 | # Specify variable tags. 14 | vartags = [] 15 | 16 | # Specify property tags. 17 | proptags = [] 18 | 19 | # ----------------------------- 20 | # Options affecting formatting. 21 | # ----------------------------- 22 | with section("format"): 23 | 24 | # Disable formatting entirely, making cmake-format a no-op 25 | disable = False 26 | 27 | # How wide to allow formatted cmake files 28 | line_width = 80 29 | 30 | # How many spaces to tab for indent 31 | tab_size = 2 32 | 33 | # If true, lines are indented using tab characters (utf-8 0x09) instead of 34 | # space characters (utf-8 0x20). In cases where the layout would 35 | # require a fractional tab character, the behavior of the fractional 36 | # indentation is governed by 37 | use_tabchars = False 38 | 39 | # If is True, then the value of this variable indicates how 40 | # fractional indentions are handled during whitespace replacement. If set to 41 | # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set 42 | # to `round-up` fractional indentation is replaced with a single tab character 43 | # (utf-8 0x09) effectively shifting the column to the next tabstop 44 | fractional_tab_policy = 'use-space' 45 | 46 | # If an argument group contains more than this many sub-groups (parg or kwarg 47 | # groups) then force it to a vertical layout. 48 | max_subgroups_hwrap = 2 49 | 50 | # If a positional argument group contains more than this many arguments, then 51 | # force it to a vertical layout. 52 | max_pargs_hwrap = 6 53 | 54 | # If a cmdline positional group consumes more than this many lines without 55 | # nesting, then invalidate the layout (and nest) 56 | max_rows_cmdline = 2 57 | 58 | # If true, separate flow control names from their parentheses with a space 59 | separate_ctrl_name_with_space = False 60 | 61 | # If true, separate function names from parentheses with a space 62 | separate_fn_name_with_space = False 63 | 64 | # If a statement is wrapped to more than one line, than dangle the closing 65 | # parenthesis on its own line. 66 | dangle_parens = False 67 | 68 | # If the trailing parenthesis must be 'dangled' on its on line, then align it 69 | # to this reference: `prefix`: the start of the statement, `prefix-indent`: 70 | # the start of the statement, plus one indentation level, `child`: align to 71 | # the column of the arguments 72 | dangle_align = 'prefix' 73 | 74 | # If the statement spelling length (including space and parenthesis) is 75 | # smaller than this amount, then force reject nested layouts. 76 | min_prefix_chars = 4 77 | 78 | # If the statement spelling length (including space and parenthesis) is larger 79 | # than the tab width by more than this amount, then force reject un-nested 80 | # layouts. 81 | max_prefix_chars = 10 82 | 83 | # If a candidate layout is wrapped horizontally but it exceeds this many 84 | # lines, then reject the layout. 85 | max_lines_hwrap = 2 86 | 87 | # What style line endings to use in the output. 88 | line_ending = 'unix' 89 | 90 | # Format command names consistently as 'lower' or 'upper' case 91 | command_case = 'canonical' 92 | 93 | # Format keywords consistently as 'lower' or 'upper' case 94 | keyword_case = 'unchanged' 95 | 96 | # A list of command names which should always be wrapped 97 | always_wrap = [] 98 | 99 | # If true, the argument lists which are known to be sortable will be sorted 100 | # lexicographicall 101 | enable_sort = True 102 | 103 | # If true, the parsers may infer whether or not an argument list is sortable 104 | # (without annotation). 105 | autosort = False 106 | 107 | # By default, if cmake-format cannot successfully fit everything into the 108 | # desired linewidth it will apply the last, most agressive attempt that it 109 | # made. If this flag is True, however, cmake-format will print error, exit 110 | # with non-zero status code, and write-out nothing 111 | require_valid_layout = False 112 | 113 | # A dictionary mapping layout nodes to a list of wrap decisions. See the 114 | # documentation for more information. 115 | layout_passes = {} 116 | 117 | # ------------------------------------------------ 118 | # Options affecting comment reflow and formatting. 119 | # ------------------------------------------------ 120 | with section("markup"): 121 | 122 | # What character to use for bulleted lists 123 | bullet_char = '*' 124 | 125 | # What character to use as punctuation after numerals in an enumerated list 126 | enum_char = '.' 127 | 128 | # If comment markup is enabled, don't reflow the first comment block in each 129 | # listfile. Use this to preserve formatting of your copyright/license 130 | # statements. 131 | first_comment_is_literal = False 132 | 133 | # If comment markup is enabled, don't reflow any comment block which matches 134 | # this (regex) pattern. Default is `None` (disabled). 135 | literal_comment_pattern = None 136 | 137 | # Regular expression to match preformat fences in comments default= 138 | # ``r'^\s*([`~]{3}[`~]*)(.*)$'`` 139 | fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$' 140 | 141 | # Regular expression to match rulers in comments default= 142 | # ``r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'`` 143 | ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' 144 | 145 | # If a comment line matches starts with this pattern then it is explicitly a 146 | # trailing comment for the preceeding argument. Default is '#<' 147 | explicit_trailing_pattern = '#<' 148 | 149 | # If a comment line starts with at least this many consecutive hash 150 | # characters, then don't lstrip() them off. This allows for lazy hash rulers 151 | # where the first hash char is not separated by space 152 | hashruler_min_length = 10 153 | 154 | # If true, then insert a space between the first hash char and remaining hash 155 | # chars in a hash ruler, and normalize its length to fill the column 156 | canonicalize_hashrulers = True 157 | 158 | # enable comment markup parsing and reflow 159 | enable_markup = True 160 | 161 | # ---------------------------- 162 | # Options affecting the linter 163 | # ---------------------------- 164 | with section("lint"): 165 | 166 | # a list of lint codes to disable 167 | disabled_codes = [] 168 | 169 | # regular expression pattern describing valid function names 170 | function_pattern = '[0-9a-z_]+' 171 | 172 | # regular expression pattern describing valid macro names 173 | macro_pattern = '[0-9A-Z_]+' 174 | 175 | # regular expression pattern describing valid names for variables with global 176 | # (cache) scope 177 | global_var_pattern = '[A-Z][0-9A-Z_]+' 178 | 179 | # regular expression pattern describing valid names for variables with global 180 | # scope (but internal semantic) 181 | internal_var_pattern = '_[A-Z][0-9A-Z_]+' 182 | 183 | # regular expression pattern describing valid names for variables with local 184 | # scope 185 | local_var_pattern = '[a-z][a-z0-9_]+' 186 | 187 | # regular expression pattern describing valid names for privatedirectory 188 | # variables 189 | private_var_pattern = '_[0-9a-z_]+' 190 | 191 | # regular expression pattern describing valid names for public directory 192 | # variables 193 | public_var_pattern = '[A-Z][0-9A-Z_]+' 194 | 195 | # regular expression pattern describing valid names for function/macro 196 | # arguments and loop variables. 197 | argument_var_pattern = '[a-z][a-z0-9_]+' 198 | 199 | # regular expression pattern describing valid names for keywords used in 200 | # functions or macros 201 | keyword_pattern = '[A-Z][0-9A-Z_]+' 202 | 203 | # In the heuristic for C0201, how many conditionals to match within a loop in 204 | # before considering the loop a parser. 205 | max_conditionals_custom_parser = 2 206 | 207 | # Require at least this many newlines between statements 208 | min_statement_spacing = 1 209 | 210 | # Require no more than this many newlines between statements 211 | max_statement_spacing = 2 212 | max_returns = 6 213 | max_branches = 12 214 | max_arguments = 5 215 | max_localvars = 15 216 | max_statements = 50 217 | 218 | # ------------------------------- 219 | # Options affecting file encoding 220 | # ------------------------------- 221 | with section("encode"): 222 | 223 | # If true, emit the unicode byte-order mark (BOM) at the start of the file 224 | emit_byteorder_mark = False 225 | 226 | # Specify the encoding of the input file. Defaults to utf-8 227 | input_encoding = 'utf-8' 228 | 229 | # Specify the encoding of the output file. Defaults to utf-8. Note that cmake 230 | # only claims to support utf-8 so be careful when using anything else 231 | output_encoding = 'utf-8' 232 | 233 | # ------------------------------------- 234 | # Miscellaneous configurations options. 235 | # ------------------------------------- 236 | with section("misc"): 237 | 238 | # A dictionary containing any per-command configuration overrides. Currently 239 | # only `command_case` is supported. 240 | per_command = {} 241 | 242 | -------------------------------------------------------------------------------- /.github/workflows/ccpp.yaml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | VERBOSE: 1 7 | 8 | jobs: 9 | build_matrix: 10 | strategy: 11 | matrix: 12 | config: 13 | [ 14 | [windows-latest, x64-windows, OFF], 15 | [macos-latest, x64-osx, ON], 16 | [ubuntu-20.04, x64-linux, ON], 17 | ] 18 | runs-on: ${{ matrix.config[0] }} 19 | steps: 20 | - uses: actions/checkout@v1 21 | - name: vcpkg build 22 | uses: johnwason/vcpkg-action@v4 23 | with: 24 | pkgs: rapidjson catch2 25 | triplet: ${{ matrix.config[1] }} 26 | token: ${{ github.token }} 27 | - name: configure 28 | run: mkdir build && cd build && cmake -DSTATICJSON_ASAN=${{ matrix.config[2] }} -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{ matrix.config[1] }} .. 29 | - name: build 30 | run: cmake --build build --config Debug 31 | - name: test 32 | run: cd build && ctest -V -C Debug 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ._* 3 | xcode/ 4 | *.backup 5 | # Prerequisites 6 | *.d 7 | 8 | # Compiled Object files 9 | *.slo 10 | *.lo 11 | *.o 12 | *.obj 13 | 14 | # Precompiled Headers 15 | *.gch 16 | *.pch 17 | 18 | # Compiled Dynamic libraries 19 | *.so 20 | *.dylib 21 | *.dll 22 | 23 | # Fortran module files 24 | *.mod 25 | *.smod 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | *.lib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.appCMakeCache.txt 37 | CMakeFiles 38 | CMakeScripts 39 | Makefile 40 | cmake_install.cmake 41 | install_manifest.txt 42 | CTestTestfile.cmake 43 | 44 | rapidjson.tgz 45 | rapidjson*/ 46 | 47 | build/ 48 | .vscode/ 49 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | dist: trusty 4 | script: "sudo apt-get install -y cmake && cmake . && make && ctest -V" 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(StaticJSON) 3 | 4 | option(STATICJSON_ENABLE_TEST "Enable building test for StaticJSON" ON) 5 | option(STATICJSON_ASAN "Enable address sanitizer on non-MSVC" OFF) 6 | 7 | set(CMAKE_CXX_STANDARD_REQUIRED 0) 8 | 9 | if(NOT CMAKE_BUILD_TYPE) 10 | set(CMAKE_BUILD_TYPE RelWithDebInfo) 11 | endif() 12 | 13 | if(MSVC) 14 | add_compile_definitions(_CRT_SECURE_NO_WARNINGS=1) 15 | else() 16 | if(STATICJSON_ASAN) 17 | add_compile_options(-fsanitize=address) 18 | add_link_options(-fsanitize=address) 19 | endif() 20 | endif() 21 | 22 | find_path(RAPIDJSON_INCLUDE_DIR rapidjson/rapidjson.h) 23 | 24 | set(SOURCE_FILES src/staticjson.cpp) 25 | add_library(staticjson ${SOURCE_FILES}) 26 | set_property(TARGET staticjson PROPERTY CXX_STANDARD 11) 27 | add_library(staticjson::staticjson ALIAS staticjson) 28 | target_include_directories( 29 | staticjson 30 | PUBLIC $ 31 | $ 32 | $ 33 | $ 34 | PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) 35 | 36 | if(STATICJSON_ENABLE_TEST) 37 | set(TARGET test_staticjson) 38 | file(GLOB SOURCES test/*.hpp test/*.cpp include/staticjson/*.hpp) 39 | add_executable(${TARGET} ${SOURCES}) 40 | target_link_libraries(${TARGET} PRIVATE staticjson) 41 | set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 17) 42 | 43 | find_package(RapidJSON CONFIG REQUIRED) 44 | target_link_libraries(${TARGET} PRIVATE rapidjson) 45 | find_package(Catch2 CONFIG REQUIRED) 46 | target_link_libraries(${TARGET} PRIVATE Catch2::Catch2 Catch2::Catch2WithMain) 47 | 48 | enable_testing() 49 | add_test( 50 | NAME ${TARGET} 51 | COMMAND ${TARGET} 52 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) 53 | endif() 54 | 55 | include(GNUInstallDirs) 56 | 57 | install( 58 | TARGETS staticjson 59 | EXPORT staticjson-targets 60 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 61 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 62 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 63 | install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/staticjson 64 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 65 | install( 66 | EXPORT staticjson-targets 67 | FILE staticjson-targets.cmake 68 | NAMESPACE staticjson:: 69 | DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/staticjson) 70 | 71 | include(CMakePackageConfigHelpers) 72 | 73 | configure_package_config_file( 74 | ${CMAKE_SOURCE_DIR}/cmake/staticjson-config.cmake.in 75 | ${CMAKE_BINARY_DIR}/cmake/staticjson-config.cmake 76 | INSTALL_DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/staticjson) 77 | 78 | install(FILES ${CMAKE_BINARY_DIR}/cmake/staticjson-config.cmake 79 | DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/staticjson) 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Siyuan Ren (netheril96@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StaticJSON 2 | 3 | Fast, direct and static typed parsing of JSON with C++. 4 | 5 | ## Overview 6 | 7 | JSON is a popular format for data exchange. Reading and writing JSON in C++, however, is nontrivial. Even with the help of libraries, one still needs to write lots of boilerplate code, and it is extremely hard to guard against all possible errors, since JSON is dynamically typed while C++ employs static typing. 8 | 9 | More importantly, manually writing such code is a violation of **DRY** principle. When manually written, the class definition, parsing and serialization code can easily become out of sync, leading to brittle code and subtle bugs. 10 | 11 | `StaticJSON` is an attempt to solve this problem by automating such process. 12 | 13 | ## Usage 14 | 15 | `StaticJSON` requires a C++11 compiler. 16 | 17 | ## Method 1: use [vcpkg](https://vcpkg.io) 18 | 19 | Run command `vcpkg install staticjson`, then in your `CMakeLists.txt`, add 20 | 21 | ```cmake 22 | find_package(staticjson CONFIG REQUIRED) 23 | target_link_libraries(${YOUR_TARGET} PRIVATE staticjson::staticjson) 24 | ``` 25 | 26 | ## Method 2: just build everything together 27 | 28 | Just drop the `include` and `src` directory into your own project and build along with other sources. It requires you to separately install [`rapidjson`](https://rapidjson.org). 29 | 30 | ## Quick start 31 | 32 | ### Builtin types 33 | 34 | ```c++ 35 | #include 36 | 37 | int builtin_test() { 38 | using namespace staticjson; 39 | std::string a = to_json_string(std::vector{1.0, 2.0, -3.1415}); 40 | std::string b = to_pretty_json_string(std::map>>{}); 41 | 42 | std::vector> data; 43 | const char* json_string = "[{\" hello \": 535353, \" world \": 849}," 44 | " {\" k \": -548343}]"; 45 | assert(from_json_string(json_string, &data, nullptr)); 46 | assert(data.size() == 2); 47 | assert(data[1][" k "] == -548343); 48 | 49 | to_pretty_json_file(stdout, data); 50 | return 0; 51 | } 52 | ``` 53 | 54 | ### Register custom class types 55 | 56 | For your own classes, you need to add some definitions first before you can use the `from_json` and `to_json` functions. There are two ways of doing this. 57 | 58 | #### Intrusive definition 59 | 60 | This way requires you to implement a special method in the class prototyped `void staticjson_init(staticjson::ObjectHandler* h)`. Example definition 61 | 62 | ```c++ 63 | struct Date 64 | { 65 | int year, month, day; 66 | 67 | void staticjson_init(ObjectHandler* h) 68 | { 69 | h->add_property("year", &year); 70 | h->add_property("month", &month); 71 | h->add_property("day", &day); 72 | h->set_flags(Flags::DisallowUnknownKey); 73 | } 74 | }; 75 | 76 | struct BlockEvent 77 | { 78 | std::uint64_t serial_number, admin_ID = 255; 79 | Date date; 80 | std::string description, details; 81 | 82 | void staticjson_init(ObjectHandler* h) 83 | { 84 | h->add_property("serial_number", &serial_number); 85 | h->add_property("administrator ID", &admin_ID, Flags::Optional); 86 | h->add_property("date", &date, Flags::Optional); 87 | h->add_property("description", &description, Flags::Optional); 88 | h->add_property("details", &details, Flags::Optional); 89 | } 90 | }; 91 | ``` 92 | 93 | ### Non-intrusive definition 94 | 95 | This requires you to overload a special function for your custom class. For example, the `Date` overload is written as 96 | 97 | ```c++ 98 | namespace staticjson 99 | { 100 | void init(Date* d, ObjectHandler* h) 101 | { 102 | h->add_property("year", &d->year); 103 | h->add_property("month", &d->month); 104 | h->add_property("day", &d->day); 105 | h->set_flags(Flags::DisallowUnknownKey); 106 | } 107 | } 108 | ``` 109 | 110 | You may need to declare `staticjson::init` as a friend function in order to access private and protected members. 111 | 112 | ### Register enumeration types 113 | 114 | Example 115 | 116 | ```c++ 117 | enum class CalendarType 118 | { 119 | Gregorian, 120 | Chinese, 121 | Jewish, 122 | Islam 123 | }; 124 | 125 | STATICJSON_DECLARE_ENUM(CalendarType, 126 | {"Gregorian", CalendarType::Gregorian}, 127 | {"Chinese", CalendarType::Chinese}, 128 | {"Jewish", CalendarType::Jewish}, 129 | {"Islam", CalendarType::Islam}) 130 | ``` 131 | 132 | This will convert the enum type to/from strings, and signal error if the string is not in the list. 133 | 134 | Note that this macro must not be instantiated inside a namespace. 135 | 136 | ## Custom conversion 137 | 138 | If you want a type to be serialized in a different way, such as a custom `Date` object as an ISO8601 string or an arbitrary precision integer as a list of 32-bit integers, you can enable the custom conversion for the type. To do so, specialize the template class in namespace `staticjson` 139 | 140 | ```c++ 141 | namespace staticjson 142 | { 143 | template <> 144 | struct Converter 145 | { 146 | typedef std::string shadow_type; 147 | // This typedef is a must. The shadow type is a C++ type 148 | // that can be directly converted to and from JSON values. 149 | 150 | static std::unique_ptr from_shadow(const shadow_type& shadow, Date& value) 151 | { 152 | bool success = value.parseISO8601(shadow); 153 | if (success) 154 | return nullptr; 155 | return std::make_unique("Invalid ISO 8601 string"); 156 | } 157 | 158 | static void to_shadow(const Date& value, shadow_type& shadow) 159 | { 160 | shadow = value.toISO8601(); 161 | } 162 | }; 163 | } 164 | 165 | ``` 166 | 167 | ## Error handling 168 | 169 | `StaticJSON` strives not to let any mismatch between the C++ type specifications and the JSON object slip. It detects and reports all kinds of errors, including type mismatch, integer out of range, floating number precision loss, required fields missing, duplicate keys etc. Many of them can be tuned on or off. It also reports an stack trace in case of error (not actual C++ exception). 170 | 171 | The third parameter of all `from_json` family of functions is a nullable pointer to `staticjson::ParseStatus` object. If present, the error information will be dumped into it. An example error message is 172 | 173 | ``` 174 | Parsing failed at offset 1000 with error code 16: 175 | Terminate parsing due to Handler error. 176 | 177 | Traceback (last call first) 178 | * Type mismatch between expected type "unsigned long long" and actual type "string" 179 | * Error at object member with name "serial_number" 180 | * Error at array element at index 0 181 | * Error at object member with name "dark_history" 182 | * Error at array element at index 1 183 | ``` 184 | 185 | ## List of builtin supported types 186 | 187 | * **Boolean types**: `bool`, `char` 188 | * **Integer types**: `int`, `unsigned int`, `long`, `unsigned long`, `long long`, `unsigned long long` 189 | * **Floating point types**: `float`, `double` 190 | * **String types**: `std::string` 191 | * **Array types**: `std::vector<•>`, `std::deque<•>`, `std::list<•>`, `std::array<•>` 192 | * **Nullable types**: `std::nullptr_t`, `std::unique_ptr<•>`, `std::shared_ptr<•>` 193 | * **Map types**: `std::{map, multimap, unordered_map, unordered_multimap}` 194 | * **Tuple types**: `std::tuple<...>` 195 | 196 | ## Dynamic typing 197 | 198 | If you need occasional escape from the rigidity of C++'s static type system, but do not want complete dynamism, you can still find the middle ground in `StaticJSON`. 199 | 200 | * You can embed a `staticjson::Document` (alias of `rapidjson::Document`) in your class/struct, which allows static typing for some class members and dynamic typing for others. Note `Document` is already nullable so do not use a smart pointer to `Document`. 201 | * You can convert a `Document` or `Value` to and from a C++ type registered in `StaticJSON`. The functions are aptly named `from_json_value`, `from_json_document`, `to_json_value`, `to_json_document`. 202 | 203 | 204 | ## Export as JSON Schema 205 | 206 | Function `export_json_schema` allows you to export the validation rules used by `StaticJSON` as JSON schema. It can then be used in other languages to do the similar validation. Note the two rules are only approximate match, because certain rules cannot be expressed in JSON schema yet, and because some languages have different treatments of numbers from C++. 207 | 208 | ## Misc 209 | 210 | The project was originally named *autojsoncxx* and requires a code generator to run. 211 | -------------------------------------------------------------------------------- /_clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: true 6 | AlignEscapedNewlinesLeft: false 7 | AlignOperands: false 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortIfStatementsOnASingleLine: false 13 | AllowShortLoopsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AlwaysBreakAfterDefinitionReturnType: false 16 | AlwaysBreakTemplateDeclarations: true 17 | AlwaysBreakBeforeMultilineStrings: false 18 | BreakBeforeBinaryOperators: All 19 | BreakBeforeTernaryOperators: true 20 | BreakConstructorInitializersBeforeComma: true 21 | BinPackParameters: false 22 | BinPackArguments: false 23 | ColumnLimit: 100 24 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 25 | ConstructorInitializerIndentWidth: 4 26 | DerivePointerAlignment: false 27 | ExperimentalAutoDetectBinPacking: false 28 | IndentCaseLabels: false 29 | IndentWrappedFunctionNames: false 30 | IndentFunctionDeclarationAfterType: false 31 | MaxEmptyLinesToKeep: 1 32 | KeepEmptyLinesAtTheStartOfBlocks: true 33 | NamespaceIndentation: Inner 34 | ObjCBlockIndentWidth: 4 35 | ObjCSpaceAfterProperty: true 36 | ObjCSpaceBeforeProtocolList: true 37 | PenaltyBreakBeforeFirstCallParameter: 19 38 | PenaltyBreakComment: 300 39 | PenaltyBreakString: 1000 40 | PenaltyBreakFirstLessLess: 120 41 | PenaltyExcessCharacter: 1000000 42 | PenaltyReturnTypeOnItsOwnLine: 60 43 | PointerAlignment: Left 44 | SpacesBeforeTrailingComments: 4 45 | Cpp11BracedListStyle: true 46 | Standard: Cpp11 47 | IndentWidth: 4 48 | TabWidth: 8 49 | UseTab: Never 50 | BreakBeforeBraces: Allman 51 | SpacesInParentheses: false 52 | SpacesInSquareBrackets: false 53 | SpacesInAngles: false 54 | SpaceInEmptyParentheses: false 55 | SpacesInCStyleCastParentheses: false 56 | SpaceAfterCStyleCast: false 57 | SpacesInContainerLiterals: true 58 | SpaceBeforeAssignmentOperators: true 59 | ContinuationIndentWidth: 4 60 | CommentPragmas: '^ IWYU pragma:' 61 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 62 | SpaceBeforeParens: ControlStatements 63 | DisableFormat: false 64 | FixNamespaceComments: false 65 | ... 66 | 67 | -------------------------------------------------------------------------------- /autojsoncxx/autojsoncxx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace autojsoncxx 8 | { 9 | using ParsingResult = staticjson::ParseStatus; 10 | 11 | using staticjson::to_json_file; 12 | using staticjson::to_json_string; 13 | using staticjson::to_pretty_json_file; 14 | using staticjson::to_pretty_json_string; 15 | 16 | namespace error = staticjson::error; 17 | 18 | template 19 | inline void to_json_string(std::string& output, const ValueType& value) 20 | { 21 | output = to_json_string(value); 22 | } 23 | 24 | template 25 | inline void to_pretty_json_string(std::string& output, const ValueType& value) 26 | { 27 | output = to_pretty_json_string(value); 28 | } 29 | 30 | template 31 | inline bool from_json_string(const char* str, ValueType& value, ParsingResult& err) 32 | { 33 | return staticjson::from_json_string(str, &value, &err); 34 | } 35 | 36 | template 37 | inline bool from_json_string(const std::string& str, ValueType& value, ParsingResult& err) 38 | { 39 | return staticjson::from_json_string(str.c_str(), &value, &err); 40 | } 41 | 42 | template 43 | inline bool from_json_file(const char* str, ValueType& value, ParsingResult& err) 44 | { 45 | return staticjson::from_json_file(str, &value, &err); 46 | } 47 | 48 | template 49 | inline bool from_json_file(const std::string& str, ValueType& value, ParsingResult& err) 50 | { 51 | return staticjson::from_json_file(str, &value, &err); 52 | } 53 | 54 | template 55 | inline bool from_json_file(std::FILE* fp, ValueType& value, ParsingResult& err) 56 | { 57 | return staticjson::from_json_file(fp, &value, &err); 58 | } 59 | 60 | template 61 | void to_document(const T& value, rapidjson::Document& doc) 62 | { 63 | staticjson::to_json_document(&doc, value, nullptr); 64 | } 65 | 66 | template 67 | bool from_document(T& value, const rapidjson::Document& doc, error::ErrorStack& errs) 68 | { 69 | staticjson::ParseStatus status; 70 | bool rc = staticjson::from_json_document(doc, &value, &status); 71 | if (status.has_error()) 72 | errs.swap(status.error_stack()); 73 | return rc; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /autojsoncxx/autojsoncxx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2014 Siyuan Ren (netheril96@gmail.com) 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | 26 | """ 27 | This script allows easier transition from StaticJSON's precursor `autojsoncxx`. 28 | 29 | Generates compatible C++ header files using StaticJSON's library. 30 | """ 31 | 32 | from __future__ import unicode_literals 33 | from __future__ import print_function 34 | 35 | import re 36 | import argparse 37 | import os 38 | import string 39 | import sys 40 | import io 41 | from numbers import Number 42 | 43 | 44 | # Python 2/3 compatibility layer 45 | is_python2 = sys.version_info.major == 2 46 | if is_python2: 47 | str = unicode 48 | 49 | # simplejson has the same interface as the standard json module, but with better error messages 50 | try: 51 | import simplejson as json 52 | except ImportError: 53 | import json 54 | 55 | 56 | # base class for all custom exceptions in this unit 57 | class InvalidDefinitionError(Exception): 58 | pass 59 | 60 | 61 | class InvalidIdentifier(InvalidDefinitionError): 62 | def __init__(self, identifier): 63 | self.identifier = identifier 64 | 65 | def __str__(self): 66 | return "Invalid string for C++ identifier: " + repr(self.identifier) 67 | 68 | 69 | class InvalidNamespace(InvalidDefinitionError): 70 | def __init__(self, namespace): 71 | self.namespace = namespace 72 | 73 | def __str__(self): 74 | return "Invalid namespace: " + repr(self.namespace) 75 | 76 | 77 | class UnrecognizedOption(InvalidDefinitionError): 78 | def __init__(self, option): 79 | self.option = option 80 | 81 | def __str__(self): 82 | return "Unrecognized option: " + repr(self.option) 83 | 84 | 85 | class UnsupportedTypeError(InvalidDefinitionError): 86 | def __init__(self, type_name): 87 | self.type_name = type_name 88 | 89 | def __str__(self): 90 | return "Unsupported C++ type: " + repr(self.type_name) 91 | 92 | 93 | NOESCAPE_CHARACTERS = bytes(string.digits + string.ascii_letters + ' ', encoding='utf8') 94 | 95 | 96 | def cstring_literal(byte_string): 97 | """convert arbitrary byte sequence into a C++ string literal by escaping every character""" 98 | if all(c in NOESCAPE_CHARACTERS for c in byte_string): 99 | return '"' + byte_string.decode() + '"' 100 | return '"' + ''.join('\\x{:02x}'.format(ord(char)) for char in byte_string) + '"' 101 | 102 | 103 | def check_identifier(identifier): 104 | if not re.match(r'^[A-Za-z_]\w*$', identifier): 105 | raise InvalidIdentifier(identifier) 106 | 107 | 108 | class ClassInfo(object): 109 | accept_options = {"name", "namespace", "parse_mode", "members", "constructor_code", "comment", "no_duplicates"} 110 | 111 | def __init__(self, record): 112 | self._name = record['name'] 113 | self._members = [MemberInfo(r) for r in record['members']] 114 | self._strict = record.get('parse_mode', '') == 'strict' 115 | self._namespace = record.get("namespace", None) 116 | self._constructor_code = record.get("constructor_code", "") 117 | self._no_duplicates = record.get("no_duplicates", False) 118 | 119 | check_identifier(self._name) 120 | 121 | if self._namespace is not None and not re.match(r'^(?:::)?[A-Za-z_]\w*(?:::[A-Za-z_]\w*)*$', self._namespace): 122 | raise InvalidNamespace(self._namespace) 123 | 124 | for op in record: 125 | if op not in ClassInfo.accept_options: 126 | raise UnrecognizedOption(op) 127 | 128 | @property 129 | def name(self): 130 | return self._name 131 | 132 | @property 133 | def qualified_name(self): 134 | if self.namespace is None: 135 | return '::' + self.name 136 | if self.namespace.startswith('::'): 137 | return self.namespace + '::' + self.name 138 | return '::' + self.namespace + '::' + self.name 139 | 140 | @property 141 | def members(self): 142 | return self._members 143 | 144 | @property 145 | def strict_parsing(self): 146 | return self._strict 147 | 148 | @property 149 | def namespace(self): 150 | return self._namespace 151 | 152 | @property 153 | def constructor_code(self): 154 | return self._constructor_code 155 | 156 | @property 157 | def no_duplicates(self): 158 | return self._no_duplicates 159 | 160 | 161 | class ClassDefinitionCodeGenerator(object): 162 | def __init__(self, class_info): 163 | self._class_info = class_info 164 | 165 | @property 166 | def class_info(self): 167 | return self._class_info 168 | 169 | def member_declarations(self): 170 | return '\n'.join(m.type_name + ' ' + m.variable_name + ';' for m in self.class_info.members) 171 | 172 | def initializer_list(self): 173 | return ', '.join('{0}({1})'.format(m.variable_name, m.constructor_args) for m in self.class_info.members) 174 | 175 | def constructor(self): 176 | return 'explicit {name}():{init} {{ {code} }}\n'.format(name=self.class_info.name, 177 | init=self.initializer_list(), 178 | code=self.class_info.constructor_code) 179 | 180 | def staticjson_init(self): 181 | class_flags = 'staticjson::Flags::Default' 182 | if not self.class_info.no_duplicates: 183 | class_flags += ' | staticjson::Flags::AllowDuplicateKey' 184 | if self.class_info.strict_parsing: 185 | class_flags += ' | staticjson::Flags::DisallowUnknownKey' 186 | 187 | return """ 188 | void staticjson_init(staticjson::ObjectHandler* h) {{ 189 | {member_flag_settings} 190 | h->set_flags({class_flags}); 191 | }} 192 | """.format( 193 | class_flags=class_flags, 194 | member_flag_settings=''.join(m.add_property_statement('h') for m in self.class_info.members) 195 | ) 196 | 197 | def class_definition(self): 198 | class_def = 'struct {name} {{\n {declarations}\n\n{constructor}\n{staticjson_init}\n \n}};' \ 199 | .format(name=self.class_info.name, declarations=self.member_declarations(), 200 | constructor=self.constructor(), 201 | staticjson_init=self.staticjson_init()) 202 | 203 | if self.class_info.namespace is not None: 204 | for space in reversed(self.class_info.namespace.split('::')): 205 | if space: 206 | class_def = 'namespace {} {{ {} }}\n'.format(space, class_def) 207 | return class_def 208 | 209 | 210 | class MemberInfo(object): 211 | accept_options = {'default', 'required', 'json_key', 'comment'} 212 | 213 | def __init__(self, record): 214 | self._record = record 215 | 216 | if '*' in self.type_name or '&' in self.type_name: 217 | raise UnsupportedTypeError(self.type_name) 218 | 219 | check_identifier(self.variable_name) 220 | 221 | if len(record) > 3: 222 | raise UnrecognizedOption(record[3:]) 223 | 224 | if len(record) == 3: 225 | for op in record[2]: 226 | if op not in MemberInfo.accept_options: 227 | raise UnrecognizedOption(op) 228 | 229 | @property 230 | def type_name(self): 231 | return self._record[0] 232 | 233 | @property 234 | def variable_name(self): 235 | return self._record[1] 236 | 237 | @property 238 | def json_key(self): 239 | try: 240 | return self._record[2]['json_key'].encode('utf-8') 241 | except (IndexError, KeyError): 242 | return self.variable_name.encode('utf-8') 243 | 244 | @property 245 | def is_required(self): 246 | try: 247 | return self._record[2]['required'] 248 | except (IndexError, KeyError): 249 | return False 250 | 251 | @property 252 | def default(self): 253 | try: 254 | return self._record[2]['default'] 255 | except (IndexError, KeyError): 256 | return None 257 | 258 | @property 259 | def constructor_args(self): 260 | return MemberInfo.cpp_repr(self.default) 261 | 262 | def add_property_statement(self, handler_name): 263 | return '{}->add_property({}, &this->{}, {});\n'.format( 264 | handler_name, cstring_literal(self.json_key), self.variable_name, 265 | 'staticjson::Flags::Default' if self.is_required else 'staticjson::Flags::Optional') 266 | 267 | @staticmethod 268 | def cpp_repr(args): 269 | if args is None: 270 | return '' 271 | elif args is True: 272 | return 'true' 273 | elif args is False: 274 | return 'false' 275 | elif isinstance(args, str): 276 | return cstring_literal(args.encode('utf-8')) 277 | elif isinstance(args, Number): 278 | return str(args) 279 | elif isinstance(args, bytes): 280 | return cstring_literal(args) 281 | else: 282 | raise UnrecognizedOption("default=" + repr(args)) 283 | 284 | 285 | def read_utf8(filename): 286 | with io.open(filename, 'rt', encoding='utf-8') as f: 287 | text = f.read() 288 | if text.startswith(u'\ufeff'): # Skip BOM 289 | text = text[1:] 290 | return text 291 | 292 | 293 | def main(): 294 | parser = argparse.ArgumentParser(description='`autojsoncxx` code generator (compatibility mode)\n' 295 | '(visit https://github.com/netheril96/StaticJSON for details)') 296 | 297 | parser.add_argument('-c', '--check', help='Compatibility flag; does nothing', 298 | action='store_true', default=False) 299 | parser.add_argument('-i', '--input', help='input name for the definition file for classes', required=True) 300 | parser.add_argument('-o', '--output', help='output name for the header file', default=None) 301 | parser.add_argument('--template', help='Compatibility flag; does nothing', default=None) 302 | args = parser.parse_args() 303 | 304 | if args.output is None: 305 | args.output = os.path.basename(args.input) 306 | args.output = os.path.splitext(args.output)[0] + '.hpp' 307 | 308 | raw_record = json.loads(read_utf8(args.input)) 309 | 310 | with io.open(args.output, 'w', encoding='utf-8') as output: 311 | output.write('#pragma once\n\n') 312 | 313 | def output_class(class_record): 314 | output.write(ClassDefinitionCodeGenerator(ClassInfo(class_record)).class_definition()) 315 | output.write('\n\n') 316 | 317 | if isinstance(raw_record, list): 318 | for r in raw_record: 319 | output_class(r) 320 | else: 321 | output_class(raw_record) 322 | 323 | 324 | if __name__ == '__main__': 325 | main() 326 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/duplicate_key.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", 49 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", 50 | "note": "the duplicate key error only occurs if your map types does not support duplicate key, e.g. `std::map`" 51 | } 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/duplicate_key_user.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "ID": 3399510046441, 33 | "dark_history": [ 34 | { 35 | "serial_number": 11932018656737597978, 36 | "administrator ID": 8027685536805674999, 37 | "date": { 38 | "year": 2013, 39 | "month": 3, 40 | "day": 8 41 | }, 42 | "description": "copyright infringement", 43 | "details": "" 44 | } 45 | ], 46 | "optional_attributes": { 47 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 48 | "Open ID": "something@somewhere.com", 49 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 50 | } 51 | } 52 | ] 53 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/hard.json: -------------------------------------------------------------------------------- 1 | [{"serial_number": 90049710466114, "details": "unknown"}, -65535, null, 2.718281828459045, {"Second": {"ID": 13478355757133566847, "optional_attributes": {"Open ID": "something@somewhere.com", "Self description": "Can you handle characters above Basic Multilingual Plane?\ud83d\ude04\ud83c\udf85 \ud83c\udf81\ud83d\udc31\u2708\ufe0f\ud83d\udc18\ud83d\udd22\ud840\udc46\ud841\udf7b\ud844\udfa6\ud844\udf20", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C"}, "block_event": null, "Third": null, "dark_history": [{"date": {"month": 3, "day": 8, "year": 2013}, "serial_number": 11932018656737597978, "details": null, "administrator ID": 8027685536805674999, "description": "copyright infringement"}], "birthday": {"month": 1, "day": 23, "year": 2001}, "nickname": "\u2192\u261b\u2205\u2206\u2135"}, "First": {"comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`", "ID": 7947402710862746952, "nickname": "bigger than bigger", "birthday": {"month": 9, "day": 2, "year": 1984}, "block_event": {"date": {"month": 12, "day": 31, "year": 1970}, "serial_number": 9097588792683265916, "details": "most likely a troll", "administrator ID": 10720293540521355122, "description": "advertisement"}}}, false] 2 | 3 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/integer_string.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": "0xa5970bbff02d9a1a", 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/map_element_mismatch.json: -------------------------------------------------------------------------------- 1 | { 2 | "First": { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | "Second": { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | }, 51 | "Third": false 52 | } 53 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/missing_required.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | } 21 | }, 22 | { 23 | "ID": 13478355757133566847, 24 | "nickname": "→☛∅∆ℵ", 25 | "birthday": { 26 | "year": 2001, 27 | "month": 1, 28 | "day": 23 29 | }, 30 | "block_event": null, 31 | "dark_history": [ 32 | { 33 | "serial_number": 11932018656737597978, 34 | "administrator ID": 8027685536805674999, 35 | "date": { 36 | "year": 2013, 37 | "month": 3 38 | }, 39 | "description": "copyright infringement", 40 | "details": "" 41 | } 42 | ], 43 | "optional_attributes": { 44 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 45 | "Open ID": "something@somewhere.com", 46 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 47 | } 48 | } 49 | ] 50 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/null_in_key.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year\u0000 I am invisible because I am behind a null": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/out_of_range.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 162751169614016163 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/single_object.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /autojsoncxx/examples/failure/unknown_field.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31, 17 | "hour": 21, 18 | "minute": 33, 19 | "second": 2 20 | }, 21 | "description": "advertisement", 22 | "details": "most likely a troll" 23 | } 24 | }, 25 | { 26 | "ID": 13478355757133566847, 27 | "nickname": "→☛∅∆ℵ", 28 | "birthday": { 29 | "year": 2001, 30 | "month": 1, 31 | "day": 23 32 | }, 33 | "block_event": null, 34 | "dark_history": [ 35 | { 36 | "serial_number": 11932018656737597978, 37 | "administrator ID": 8027685536805674999, 38 | "date": { 39 | "year": 2013, 40 | "month": 3, 41 | "day": 8 42 | }, 43 | "description": "copyright infringement", 44 | "details": "" 45 | } 46 | ], 47 | "optional_attributes": { 48 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 49 | "Open ID": "something@somewhere.com", 50 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 51 | } 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /autojsoncxx/examples/success/hard.json: -------------------------------------------------------------------------------- 1 | [{"serial_number": 90049710466114, "details": "unknown"}, -65535, null, 2.718281828459045, {"Second": {"ID": 13478355757133566847, "optional_attributes": {"Open ID": "something@somewhere.com", "Self description": "Can you handle characters above Basic Multilingual Plane?\ud83d\ude04\ud83c\udf85 \ud83c\udf81\ud83d\udc31\u2708\ufe0f\ud83d\udc18\ud83d\udd22\ud840\udc46\ud841\udf7b\ud844\udfa6\ud844\udf20", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C"}, "block_event": null, "Third": null, "dark_history": [{"date": {"month": 3, "day": 8, "year": 2013}, "serial_number": 11932018656737597978, "details": "", "administrator ID": 8027685536805674999, "description": "copyright infringement"}], "birthday": {"month": 1, "day": 23, "year": 2001}, "nickname": "\u2192\u261b\u2205\u2206\u2135"}, "First": {"comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`", "ID": 7947402710862746952, "nickname": "bigger than bigger", "birthday": {"month": 9, "day": 2, "year": 1984}, "block_event": {"date": {"month": 12, "day": 31, "year": 1970}, "serial_number": 9097588792683265916, "details": "most likely a troll", "administrator ID": 10720293540521355122, "description": "advertisement"}}}, false] 2 | -------------------------------------------------------------------------------- /autojsoncxx/examples/success/user_array.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /autojsoncxx/examples/success/user_array_compact.json: -------------------------------------------------------------------------------- 1 | [{"ID":7947402710862746952,"nickname":"bigger than bigger","birthday":{"year":1984,"month":9,"day":2},"block_event":{"serial_number":9097588792683265916,"administrator ID":10720293540521355122,"date":{"year":1970,"month":12,"day":31},"description":"advertisement","details":"most likely a troll"},"dark_history":[],"optional_attributes":{}},{"ID":13478355757133566847,"nickname":"→☛∅∆ℵ","birthday":{"year":2001,"month":1,"day":23},"block_event":null,"dark_history":[{"serial_number":11932018656737597978,"administrator ID":8027685536805674999,"date":{"year":2013,"month":3,"day":8},"description":"copyright infringement","details":""}],"optional_attributes":{"Auth-Token":"45F13704-A60D-4D44-B161-89BAB88E528C","Open ID":"something@somewhere.com","Self description":"Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠"}}] -------------------------------------------------------------------------------- /autojsoncxx/examples/success/user_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "First": { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | "Second": { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /autojsoncxx/userdef.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace config 4 | { 5 | struct Date 6 | { 7 | int year; 8 | int month; 9 | int day; 10 | 11 | explicit Date() : year(), month(), day() {} 12 | 13 | void staticjson_init(staticjson::ObjectHandler* h) 14 | { 15 | h->add_property("year", &this->year, staticjson::Flags::Default); 16 | h->add_property("month", &this->month, staticjson::Flags::Default); 17 | h->add_property("day", &this->day, staticjson::Flags::Default); 18 | 19 | h->set_flags(staticjson::Flags::Default | staticjson::Flags::AllowDuplicateKey 20 | | staticjson::Flags::DisallowUnknownKey); 21 | } 22 | }; 23 | } 24 | 25 | namespace config 26 | { 27 | namespace event 28 | { 29 | struct BlockEvent 30 | { 31 | unsigned long long serial_number; 32 | unsigned long long admin_ID; 33 | Date date; 34 | std::string description; 35 | std::string details; 36 | 37 | explicit BlockEvent() 38 | : serial_number() 39 | , admin_ID(255) 40 | , date() 41 | , description("\x2f\x2a\x20\x69\x6e\x69\x74\x20\x2a\x2f\x20\x74\x72\x79\x69\x6e\x67\x20" 42 | "\x74\x6f\x20\x6d\x65\x73\x73\x20\x75\x70\x20\x77\x69\x74\x68\x20\x74\x68" 43 | "\x65\x20\x63\x6f\x64\x65\x20\x67\x65\x6e\x65\x72\x61\x74\x6f\x72") 44 | , details() 45 | { 46 | date.year = 1970; 47 | date.month = 1; 48 | date.day = 1; /* Assign date to the UNIX epoch */ 49 | } 50 | 51 | void staticjson_init(staticjson::ObjectHandler* h) 52 | { 53 | h->add_property("\x73\x65\x72\x69\x61\x6c\x5f\x6e\x75\x6d\x62\x65\x72", 54 | &this->serial_number, 55 | staticjson::Flags::Default); 56 | h->add_property("administrator ID", &this->admin_ID, staticjson::Flags::Optional); 57 | h->add_property("date", &this->date, staticjson::Flags::Optional); 58 | h->add_property("description", &this->description, staticjson::Flags::Optional); 59 | h->add_property("details", &this->details, staticjson::Flags::Optional); 60 | 61 | h->set_flags(staticjson::Flags::Default | staticjson::Flags::AllowDuplicateKey); 62 | } 63 | }; 64 | } 65 | } 66 | 67 | namespace config 68 | { 69 | struct User 70 | { 71 | unsigned long long ID; 72 | std::string nickname; 73 | Date birthday; 74 | std::shared_ptr block_event; 75 | std::vector dark_history; 76 | std::map optional_attributes; 77 | 78 | explicit User() 79 | : ID() 80 | , nickname("\xe2\x9d\xb6\xe2\x9d\xb7\xe2\x9d\xb8") 81 | , birthday() 82 | , block_event() 83 | , dark_history() 84 | , optional_attributes() 85 | { 86 | } 87 | 88 | void staticjson_init(staticjson::ObjectHandler* h) 89 | { 90 | h->add_property("ID", &this->ID, staticjson::Flags::Default); 91 | h->add_property("nickname", &this->nickname, staticjson::Flags::Default); 92 | h->add_property("birthday", &this->birthday, staticjson::Flags::Optional); 93 | h->add_property("\x62\x6c\x6f\x63\x6b\x5f\x65\x76\x65\x6e\x74", 94 | &this->block_event, 95 | staticjson::Flags::Optional); 96 | h->add_property("\x64\x61\x72\x6b\x5f\x68\x69\x73\x74\x6f\x72\x79", 97 | &this->dark_history, 98 | staticjson::Flags::Optional); 99 | h->add_property( 100 | "\x6f\x70\x74\x69\x6f\x6e\x61\x6c\x5f\x61\x74\x74\x72\x69\x62\x75\x74\x65\x73", 101 | &this->optional_attributes, 102 | staticjson::Flags::Optional); 103 | 104 | h->set_flags(staticjson::Flags::Default); 105 | } 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /autojsoncxx/userdef.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Date", 4 | "namespace": "config", 5 | "parse_mode": "strict", 6 | "members": [ 7 | ["int", "year", {"required": true}], 8 | ["int", "month", {"required": true}], 9 | ["int", "day", {"required": true}] 10 | ] 11 | }, 12 | 13 | { 14 | "name": "BlockEvent", 15 | "namespace": "config::event", 16 | "members": [ 17 | ["unsigned long long", "serial_number", {"required": true}], 18 | ["unsigned long long", "admin_ID", {"json_key": "administrator ID", "default": 255}], 19 | ["Date", "date"], 20 | ["std::string", "description", {"default": "/* init */ trying to mess up with the code generator"}], 21 | ["std::string", "details"] 22 | ], 23 | "constructor_code": "date.year = 1970; date.month = 1; date.day = 1; /* Assign date to the UNIX epoch */" 24 | }, 25 | 26 | { 27 | "name": "User", 28 | "namespace": "config", 29 | "no_duplicates": true, 30 | "members": [ 31 | ["unsigned long long", "ID", {"required": true}], 32 | ["std::string", "nickname", {"required": true, "default": "❶❷❸"} ], 33 | ["Date", "birthday"], 34 | ["std::shared_ptr", "block_event"], 35 | ["std::vector", "dark_history"], 36 | ["std::map", "optional_attributes"] 37 | ] 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /cmake/staticjson-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | include(CMakeFindDependencyMacro) 3 | find_dependency(rapidjson CONFIG) 4 | 5 | if(NOT TARGET staticjson::staticjson) 6 | include(${CMAKE_CURRENT_LIST_DIR}/staticjson-targets.cmake) 7 | endif() 8 | -------------------------------------------------------------------------------- /examples/failure/duplicate_key.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", 49 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", 50 | "note": "the duplicate key error only occurs if your map types does not support duplicate key, e.g. `std::map`" 51 | } 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /examples/failure/duplicate_key_user.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "ID": 3399510046441, 33 | "dark_history": [ 34 | { 35 | "serial_number": 11932018656737597978, 36 | "administrator ID": 8027685536805674999, 37 | "date": { 38 | "year": 2013, 39 | "month": 3, 40 | "day": 8 41 | }, 42 | "description": "copyright infringement", 43 | "details": "" 44 | } 45 | ], 46 | "optional_attributes": { 47 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 48 | "Open ID": "something@somewhere.com", 49 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 50 | } 51 | } 52 | ] 53 | -------------------------------------------------------------------------------- /examples/failure/hard.json: -------------------------------------------------------------------------------- 1 | [{"serial_number": 90049710466114, "details": "unknown"}, -65535, null, 2.718281828459045, {"Second": {"ID": 13478355757133566847, "optional_attributes": {"Open ID": "something@somewhere.com", "Self description": "Can you handle characters above Basic Multilingual Plane?\ud83d\ude04\ud83c\udf85 \ud83c\udf81\ud83d\udc31\u2708\ufe0f\ud83d\udc18\ud83d\udd22\ud840\udc46\ud841\udf7b\ud844\udfa6\ud844\udf20", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C"}, "block_event": null, "Third": null, "dark_history": [{"date": {"month": 3, "day": 8, "year": 2013}, "serial_number": 11932018656737597978, "details": null, "administrator ID": 8027685536805674999, "description": "copyright infringement"}], "birthday": {"month": 1, "day": 23, "year": 2001}, "nickname": "\u2192\u261b\u2205\u2206\u2135"}, "First": {"comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`", "ID": 7947402710862746952, "nickname": "bigger than bigger", "birthday": {"month": 9, "day": 2, "year": 1984}, "block_event": {"date": {"month": 12, "day": 31, "year": 1970}, "serial_number": 9097588792683265916, "details": "most likely a troll", "administrator ID": 10720293540521355122, "description": "advertisement"}}}, false] 2 | 3 | -------------------------------------------------------------------------------- /examples/failure/integer_string.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": "0xa5970bbff02d9a1a", 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /examples/failure/invalid_enum.json: -------------------------------------------------------------------------------- 1 | { 2 | "First": { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "dark_event": { 22 | "serial_number": 9876543210123456789, 23 | "administrator ID": 11223344556677889900, 24 | "date": { 25 | "year": 1970, 26 | "month": 12, 27 | "day": 31 28 | }, 29 | "description": "promote", 30 | "details": "at least like a troll", 31 | "flags": null 32 | }, 33 | "alternate_history": [ 34 | null, 35 | { 36 | "serial_number": 1123581321345589, 37 | "administrator ID": 1123581321345589, 38 | "date": { 39 | "year": 1970, 40 | "month": 12, 41 | "day": 31 42 | }, 43 | "description": "redacted", 44 | "details": "redacted", 45 | "flags": "x" 46 | } 47 | ], 48 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 49 | }, 50 | "Second": { 51 | "ID": 13478355757133566847, 52 | "nickname": "→☛∅∆ℵ", 53 | "birthday": { 54 | "year": 2001, 55 | "month": 1, 56 | "day": 23 57 | }, 58 | "block_event": null, 59 | "dark_history": [ 60 | { 61 | "serial_number": 11932018656737597978, 62 | "administrator ID": 8027685536805674999, 63 | "date": { 64 | "year": 2013, 65 | "month": 3, 66 | "day": 8, 67 | "type": "West" 68 | }, 69 | "description": "copyright infringement", 70 | "details": "" 71 | } 72 | ], 73 | "optional_attributes": { 74 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 75 | "Open ID": "something@somewhere.com", 76 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/failure/map_element_mismatch.json: -------------------------------------------------------------------------------- 1 | { 2 | "First": { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | "Second": { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | }, 51 | "Third": false 52 | } 53 | -------------------------------------------------------------------------------- /examples/failure/missing_required.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | } 21 | }, 22 | { 23 | "ID": 13478355757133566847, 24 | "nickname": "→☛∅∆ℵ", 25 | "birthday": { 26 | "year": 2001, 27 | "month": 1, 28 | "day": 23 29 | }, 30 | "block_event": null, 31 | "dark_history": [ 32 | { 33 | "serial_number": 11932018656737597978, 34 | "administrator ID": 8027685536805674999, 35 | "date": { 36 | "year": 2013, 37 | "month": 3 38 | }, 39 | "description": "copyright infringement", 40 | "details": "" 41 | } 42 | ], 43 | "optional_attributes": { 44 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 45 | "Open ID": "something@somewhere.com", 46 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 47 | } 48 | } 49 | ] 50 | -------------------------------------------------------------------------------- /examples/failure/null_in_key.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year\u0000 I am invisible because I am behind a null": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /examples/failure/out_of_range.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 162751169614016163 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 22 | }, 23 | { 24 | "ID": 13478355757133566847, 25 | "nickname": "→☛∅∆ℵ", 26 | "birthday": { 27 | "year": 2001, 28 | "month": 1, 29 | "day": 23 30 | }, 31 | "block_event": null, 32 | "dark_history": [ 33 | { 34 | "serial_number": 11932018656737597978, 35 | "administrator ID": 8027685536805674999, 36 | "date": { 37 | "year": 2013, 38 | "month": 3, 39 | "day": 8 40 | }, 41 | "description": "copyright infringement", 42 | "details": "" 43 | } 44 | ], 45 | "optional_attributes": { 46 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 47 | "Open ID": "something@somewhere.com", 48 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /examples/failure/single_object.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/failure/tensor_length_error.json: -------------------------------------------------------------------------------- 1 | [ 2 | [] 3 | ] 4 | -------------------------------------------------------------------------------- /examples/failure/tensor_type_mismatch.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["1", 2, 3] 3 | ] 4 | -------------------------------------------------------------------------------- /examples/failure/unknown_field.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31, 17 | "hour": 21, 18 | "minute": 33, 19 | "second": 2 20 | }, 21 | "description": "advertisement", 22 | "details": "most likely a troll" 23 | } 24 | }, 25 | { 26 | "ID": 13478355757133566847, 27 | "nickname": "→☛∅∆ℵ", 28 | "birthday": { 29 | "year": 2001, 30 | "month": 1, 31 | "day": 23 32 | }, 33 | "block_event": null, 34 | "dark_history": [ 35 | { 36 | "serial_number": 11932018656737597978, 37 | "administrator ID": 8027685536805674999, 38 | "date": { 39 | "year": 2013, 40 | "month": 3, 41 | "day": 8 42 | }, 43 | "description": "copyright infringement", 44 | "details": "" 45 | } 46 | ], 47 | "optional_attributes": { 48 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 49 | "Open ID": "something@somewhere.com", 50 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 51 | } 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /examples/success/hard.json: -------------------------------------------------------------------------------- 1 | [{"serial_number": 90049710466114, "details": "unknown"}, -65535, null, 2.718281828459045, {"Second": {"ID": 13478355757133566847, "optional_attributes": {"Open ID": "something@somewhere.com", "Self description": "Can you handle characters above Basic Multilingual Plane?\ud83d\ude04\ud83c\udf85 \ud83c\udf81\ud83d\udc31\u2708\ufe0f\ud83d\udc18\ud83d\udd22\ud840\udc46\ud841\udf7b\ud844\udfa6\ud844\udf20", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C"}, "block_event": null, "Third": null, "dark_history": [{"date": {"month": 3, "day": 8, "year": 2013}, "serial_number": 11932018656737597978, "details": "", "administrator ID": 8027685536805674999, "description": "copyright infringement"}], "birthday": {"month": 1, "day": 23, "year": 2001}, "nickname": "\u2192\u261b\u2205\u2206\u2135"}, "First": {"comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`", "ID": 7947402710862746952, "nickname": "bigger than bigger", "birthday": {"month": 9, "day": 2, "year": 1984}, "block_event": {"date": {"month": 12, "day": 31, "year": 1970}, "serial_number": 9097588792683265916, "details": "most likely a troll", "administrator ID": 10720293540521355122, "description": "advertisement"}}}, false] 2 | -------------------------------------------------------------------------------- /examples/success/tensor.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | [1, 2, 3], 4 | [4, 5, 6], 5 | [7, 8, 0] 6 | ], 7 | [ 8 | [1.0, 2.0], 9 | [3.0, 4.0], 10 | [5.0, 6.0] 11 | ], 12 | [ 13 | [4.44], 14 | [0.9], 15 | [8.024] 16 | ], 17 | [[], [], []] 18 | ] 19 | -------------------------------------------------------------------------------- /examples/success/user_array.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2, 9 | "type": "Jewish" 10 | }, 11 | "block_event": { 12 | "serial_number": 9097588792683265916, 13 | "administrator ID": 10720293540521355122, 14 | "date": { 15 | "year": 1970, 16 | "month": 12, 17 | "day": 31, 18 | "type": "Chinese" 19 | }, 20 | "description": "advertisement", 21 | "details": "most likely a troll" 22 | }, 23 | "dark_event": { 24 | "serial_number": 9876543210123456789, 25 | "administrator ID": 11223344556677889900, 26 | "date": { 27 | "year": 1970, 28 | "month": 12, 29 | "day": 31 30 | }, 31 | "description": "promote", 32 | "details": "at least like a troll", 33 | "flags": null 34 | }, 35 | "alternate_history": [ 36 | null, 37 | { 38 | "serial_number": 1123581321345589, 39 | "administrator ID": 1123581321345589, 40 | "date": { 41 | "year": 1970, 42 | "month": 12, 43 | "day": 31 44 | }, 45 | "description": "redacted", 46 | "details": "redacted", 47 | "flags": "x" 48 | } 49 | ], 50 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 51 | }, 52 | { 53 | "ID": 13478355757133566847, 54 | "nickname": "→☛∅∆ℵ", 55 | "birthday": { 56 | "year": 2001, 57 | "month": 1, 58 | "day": 23 59 | }, 60 | "block_event": null, 61 | "dark_history": [ 62 | { 63 | "serial_number": 11932018656737597978, 64 | "administrator ID": 8027685536805674999, 65 | "date": { 66 | "year": 2013, 67 | "month": 3, 68 | "day": 8 69 | }, 70 | "description": "copyright infringement", 71 | "details": "" 72 | } 73 | ], 74 | "optional_attributes": { 75 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 76 | "Open ID": "something@somewhere.com", 77 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 78 | } 79 | }, 80 | { 81 | "ID": 994851, 82 | "nickname": "bigger than bigger", 83 | "birthday": { 84 | "year": 1984, 85 | "month": 9, 86 | "day": 2 87 | }, 88 | "block_event": { 89 | "serial_number": 75350019499420, 90 | "administrator ID": 1072029354, 91 | "date": { 92 | "year": 1993, 93 | "month": 9, 94 | "day": 7 95 | }, 96 | "description": "advertisement", 97 | "details": "hehe" 98 | }, 99 | "auxiliary": [ 100 | 3, [[4.0, 5.0], [9.2, 3.3]], true 101 | ] 102 | } 103 | ] 104 | -------------------------------------------------------------------------------- /examples/success/user_array_compact.json: -------------------------------------------------------------------------------- 1 | [{"ID":7947402710862746952,"nickname":"bigger than bigger","birthday":{"year":1984,"month":9,"day":2},"block_event":{"serial_number":9097588792683265916,"administrator ID":10720293540521355122,"date":{"year":1970,"month":12,"day":31},"description":"advertisement","details":"most likely a troll"},"dark_history":[],"optional_attributes":{}},{"ID":13478355757133566847,"nickname":"→☛∅∆ℵ","birthday":{"year":2001,"month":1,"day":23},"block_event":null,"dark_history":[{"serial_number":11932018656737597978,"administrator ID":8027685536805674999,"date":{"year":2013,"month":3,"day":8},"description":"copyright infringement","details":""}],"optional_attributes":{"Auth-Token":"45F13704-A60D-4D44-B161-89BAB88E528C","Open ID":"something@somewhere.com","Self description":"Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠"}}] -------------------------------------------------------------------------------- /examples/success/user_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "First": { 3 | "ID": 7947402710862746952, 4 | "nickname": "bigger than bigger", 5 | "birthday": { 6 | "year": 1984, 7 | "month": 9, 8 | "day": 2 9 | }, 10 | "block_event": { 11 | "serial_number": 9097588792683265916, 12 | "administrator ID": 10720293540521355122, 13 | "date": { 14 | "year": 1970, 15 | "month": 12, 16 | "day": 31 17 | }, 18 | "description": "advertisement", 19 | "details": "most likely a troll" 20 | }, 21 | "dark_event": { 22 | "serial_number": 9876543210123456789, 23 | "administrator ID": 11223344556677889900, 24 | "date": { 25 | "year": 1970, 26 | "month": 12, 27 | "day": 31 28 | }, 29 | "description": "promote", 30 | "details": "at least like a troll", 31 | "flags": null 32 | }, 33 | "alternate_history": [ 34 | null, 35 | { 36 | "serial_number": 1123581321345589, 37 | "administrator ID": 1123581321345589, 38 | "date": { 39 | "year": 1970, 40 | "month": 12, 41 | "day": 31 42 | }, 43 | "description": "redacted", 44 | "details": "redacted", 45 | "flags": "x" 46 | } 47 | ], 48 | "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" 49 | }, 50 | "Second": { 51 | "ID": 13478355757133566847, 52 | "nickname": "→☛∅∆ℵ", 53 | "birthday": { 54 | "year": 2001, 55 | "month": 1, 56 | "day": 23 57 | }, 58 | "block_event": null, 59 | "dark_history": [ 60 | { 61 | "serial_number": 11932018656737597978, 62 | "administrator ID": 8027685536805674999, 63 | "date": { 64 | "year": 2013, 65 | "month": 3, 66 | "day": 8 67 | }, 68 | "description": "copyright infringement", 69 | "details": "" 70 | } 71 | ], 72 | "optional_attributes": { 73 | "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", 74 | "Open ID": "something@somewhere.com", 75 | "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd $(dirname $0) 3 | GLOBIGNORE="include/staticjson/optional_support.hpp" 4 | clang-format -i --style=File autojsoncxx/*.hpp include/staticjson/*.hpp src/*.cpp test/*.cpp test/myarray.hpp 5 | -------------------------------------------------------------------------------- /include/staticjson/basic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace staticjson 15 | { 16 | struct NonMobile 17 | { 18 | NonMobile() {} 19 | ~NonMobile() {} 20 | NonMobile(const NonMobile&) = delete; 21 | NonMobile(NonMobile&&) = delete; 22 | NonMobile& operator=(const NonMobile&) = delete; 23 | NonMobile& operator=(NonMobile&&) = delete; 24 | }; 25 | 26 | typedef unsigned int SizeType; 27 | 28 | // This class is not thread safe, so please set all values at startup or when single threaded. 29 | class GlobalConfig : private NonMobile 30 | { 31 | public: 32 | static GlobalConfig* getInstance() noexcept; 33 | SizeType getMemoryChunkSize() const noexcept { return memoryChunkSize; } 34 | void setMemoryChunkSize(SizeType value) noexcept { memoryChunkSize = value; } 35 | void setMaxLeaves(SizeType maxNum) noexcept 36 | { 37 | maxLeaves = maxNum; 38 | _isMaxLeavesSet = true; 39 | } 40 | void setMaxDepth(SizeType maxDep) noexcept 41 | { 42 | maxDepth = maxDep; 43 | _isMaxDepthSet = true; 44 | } 45 | SizeType getMaxDepth() const noexcept { return maxDepth; } 46 | SizeType getMaxLeaves() const noexcept { return maxLeaves; } 47 | bool isMaxLeavesSet() const noexcept { return _isMaxLeavesSet; } 48 | bool isMaxDepthSet() const noexcept { return _isMaxDepthSet; } 49 | void unsetMaxLeavesFlag() noexcept 50 | { 51 | _isMaxLeavesSet = false; 52 | maxLeaves = UINT_MAX; 53 | } 54 | void unsetMaxDepthFlag() noexcept 55 | { 56 | _isMaxDepthSet = false; 57 | maxDepth = UINT_MAX; 58 | } 59 | 60 | private: 61 | GlobalConfig() {} 62 | bool _isMaxLeavesSet = false; 63 | bool _isMaxDepthSet = false; 64 | SizeType maxLeaves = UINT_MAX; 65 | SizeType maxDepth = UINT_MAX; 66 | SizeType memoryChunkSize = 1000; 67 | }; 68 | 69 | class IHandler 70 | { 71 | public: 72 | IHandler() {} 73 | 74 | virtual ~IHandler(); 75 | 76 | virtual bool Null() = 0; 77 | 78 | virtual bool Bool(bool) = 0; 79 | 80 | virtual bool Int(int) = 0; 81 | 82 | virtual bool Uint(unsigned) = 0; 83 | 84 | virtual bool Int64(std::int64_t) = 0; 85 | 86 | virtual bool Uint64(std::uint64_t) = 0; 87 | 88 | virtual bool Double(double) = 0; 89 | 90 | virtual bool String(const char*, SizeType, bool) = 0; 91 | 92 | virtual bool StartObject() = 0; 93 | 94 | virtual bool Key(const char*, SizeType, bool) = 0; 95 | 96 | virtual bool EndObject(SizeType) = 0; 97 | 98 | virtual bool StartArray() = 0; 99 | 100 | virtual bool EndArray(SizeType) = 0; 101 | 102 | virtual bool RawNumber(const char*, SizeType, bool); 103 | 104 | virtual void prepare_for_reuse() = 0; 105 | }; 106 | 107 | using rapidjson::Document; 108 | using rapidjson::Value; 109 | 110 | typedef rapidjson::MemoryPoolAllocator<> MemoryPoolAllocator; 111 | 112 | class BaseHandler : public IHandler, private NonMobile 113 | { 114 | friend class NullableHandler; 115 | 116 | protected: 117 | std::unique_ptr the_error; 118 | bool parsed = false; 119 | 120 | protected: 121 | bool set_out_of_range(const char* actual_type); 122 | bool set_type_mismatch(const char* actual_type); 123 | 124 | virtual void reset() {} 125 | 126 | public: 127 | BaseHandler() {} 128 | 129 | virtual ~BaseHandler(); 130 | 131 | virtual std::string type_name() const = 0; 132 | 133 | virtual bool Null() override { return set_type_mismatch("null"); } 134 | 135 | virtual bool Bool(bool) override { return set_type_mismatch("bool"); } 136 | 137 | virtual bool Int(int) override { return set_type_mismatch("int"); } 138 | 139 | virtual bool Uint(unsigned) override { return set_type_mismatch("unsigned"); } 140 | 141 | virtual bool Int64(std::int64_t) override { return set_type_mismatch("int64_t"); } 142 | 143 | virtual bool Uint64(std::uint64_t) override { return set_type_mismatch("uint64_t"); } 144 | 145 | virtual bool Double(double) override { return set_type_mismatch("double"); } 146 | 147 | virtual bool String(const char*, SizeType, bool) override 148 | { 149 | return set_type_mismatch("string"); 150 | } 151 | 152 | virtual bool StartObject() override { return set_type_mismatch("object"); } 153 | 154 | virtual bool Key(const char*, SizeType, bool) override { return set_type_mismatch("object"); } 155 | 156 | virtual bool EndObject(SizeType) override { return set_type_mismatch("object"); } 157 | 158 | virtual bool StartArray() override { return set_type_mismatch("array"); } 159 | 160 | virtual bool EndArray(SizeType) override { return set_type_mismatch("array"); } 161 | 162 | virtual bool has_error() const { return bool(the_error); } 163 | 164 | virtual bool reap_error(ErrorStack& errs) 165 | { 166 | if (!the_error) 167 | return false; 168 | errs.push(the_error.release()); 169 | return true; 170 | } 171 | 172 | bool is_parsed() const { return parsed; } 173 | 174 | void prepare_for_reuse() override 175 | { 176 | the_error.reset(); 177 | parsed = false; 178 | reset(); 179 | } 180 | 181 | virtual bool write(IHandler* output) const = 0; 182 | 183 | virtual void generate_schema(Value& output, MemoryPoolAllocator& alloc) const = 0; 184 | }; 185 | 186 | struct Flags 187 | { 188 | static const unsigned Default = 0x0, AllowDuplicateKey = 0x1, Optional = 0x2, IgnoreRead = 0x4, 189 | IgnoreWrite = 0x8, DisallowUnknownKey = 0x10; 190 | }; 191 | 192 | // Forward declaration 193 | template 194 | class Handler; 195 | 196 | namespace mempool 197 | { 198 | [[noreturn]] void throw_bad_alloc(); 199 | rapidjson::CrtAllocator& get_crt_allocator() noexcept; 200 | 201 | template 202 | class PooledAllocator 203 | { 204 | private: 205 | MemoryPoolAllocator* pool; 206 | 207 | template 208 | friend class PooledAllocator; 209 | 210 | public: 211 | using value_type = T; 212 | using propagate_on_container_copy_assignment = std::true_type; 213 | using propagate_on_container_move_assignment = std::true_type; 214 | using propagate_on_container_swap = std::true_type; 215 | 216 | explicit PooledAllocator(MemoryPoolAllocator* pool) noexcept : pool(pool) {} 217 | 218 | template 219 | PooledAllocator(const PooledAllocator& other) noexcept : pool(other.pool) 220 | { 221 | } 222 | template 223 | bool operator==(const PooledAllocator& other) const noexcept 224 | { 225 | return pool == other.pool; 226 | } 227 | template 228 | bool operator!=(const PooledAllocator& other) const noexcept 229 | { 230 | return pool != other.pool; 231 | } 232 | T* allocate(const size_t n) const 233 | { 234 | if (!n) 235 | { 236 | return nullptr; 237 | } 238 | auto ptr = static_cast(pool->Malloc(n * sizeof(T))); 239 | if (!ptr) 240 | { 241 | throw_bad_alloc(); 242 | } 243 | return ptr; 244 | } 245 | void deallocate(T* const p, size_t) const noexcept 246 | { 247 | if (p) 248 | { 249 | pool->Free(p); 250 | } 251 | } 252 | }; 253 | 254 | template 255 | using Map = std::map, PooledAllocator>>; 256 | 257 | template 258 | using Stack = std::stack>>; 259 | 260 | using String = std::basic_string, PooledAllocator>; 261 | 262 | inline std::string to_std_string(const String& str) { return {str.data(), str.size()}; } 263 | 264 | template 265 | struct PooledDeleter 266 | { 267 | void operator()(T* ptr) const noexcept 268 | { 269 | if (!ptr) 270 | { 271 | return; 272 | } 273 | ptr->~T(); 274 | static_assert(!MemoryPoolAllocator::kNeedFree, 275 | "MemoryPoolAllocator must not need freeing"); 276 | } 277 | }; 278 | 279 | template 280 | using UniquePtr = std::unique_ptr>; 281 | 282 | template 283 | T* pooled_new(MemoryPoolAllocator& pool, Args&&... args) 284 | { 285 | auto storage = pool.Malloc(sizeof(T)); 286 | static_assert(!MemoryPoolAllocator::kNeedFree, 287 | "MemoryPoolAllocator must not need freeing or we will need to handle " 288 | "potential exceptions in the construction of T"); 289 | new (storage) T(std::forward(args)...); 290 | return static_cast(storage); 291 | } 292 | } 293 | 294 | class ObjectHandler : public BaseHandler 295 | { 296 | protected: 297 | struct FlaggedHandler 298 | { 299 | mempool::UniquePtr handler; 300 | unsigned flags; 301 | }; 302 | 303 | protected: 304 | MemoryPoolAllocator memory_pool_allocator; 305 | mempool::Map internals; 306 | FlaggedHandler* current = nullptr; 307 | mempool::String current_name; 308 | int depth = 0; 309 | unsigned flags = Flags::Default; 310 | unsigned int jsonDepth = 0; 311 | // true if last symbol is '[', otherwise false 312 | bool lastLeafStat = false; 313 | SizeType totalLeaves = 0; 314 | // save the number of object or array 315 | mempool::Stack leavesStack; 316 | 317 | protected: 318 | void postinit(); 319 | bool precheck(const char* type); 320 | bool postcheck(bool success); 321 | void set_missing_required(const std::string& name); 322 | void add_handler(mempool::String&&, FlaggedHandler&&); 323 | void reset() override; 324 | 325 | private: 326 | bool StartCheckMaxDepthMaxLeaves(bool isArray); 327 | bool EndCheckMaxDepthMaxLeaves(SizeType sz, bool isArray); 328 | 329 | template 330 | void add_property(mempool::String name, T* pointer, unsigned flags_ = Flags::Default) 331 | { 332 | FlaggedHandler fh; 333 | fh.handler.reset(mempool::pooled_new>(memory_pool_allocator, pointer)); 334 | fh.flags = flags_; 335 | add_handler(std::move(name), std::move(fh)); 336 | } 337 | 338 | public: 339 | ObjectHandler(); 340 | 341 | ~ObjectHandler(); 342 | 343 | std::string type_name() const override; 344 | 345 | virtual bool Null() override; 346 | 347 | virtual bool Bool(bool) override; 348 | 349 | virtual bool Int(int) override; 350 | 351 | virtual bool Uint(unsigned) override; 352 | 353 | virtual bool Int64(std::int64_t) override; 354 | 355 | virtual bool Uint64(std::uint64_t) override; 356 | 357 | virtual bool Double(double) override; 358 | 359 | virtual bool String(const char*, SizeType, bool) override; 360 | 361 | virtual bool StartObject() override; 362 | 363 | virtual bool Key(const char*, SizeType, bool) override; 364 | 365 | virtual bool EndObject(SizeType) override; 366 | 367 | virtual bool StartArray() override; 368 | 369 | virtual bool EndArray(SizeType) override; 370 | 371 | virtual bool reap_error(ErrorStack&) override; 372 | 373 | virtual bool write(IHandler* output) const override; 374 | 375 | virtual void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override; 376 | 377 | unsigned get_flags() const { return flags; } 378 | 379 | void set_flags(unsigned f) { flags = f; } 380 | 381 | const MemoryPoolAllocator& get_memory_pool() const noexcept { return memory_pool_allocator; } 382 | 383 | template 384 | void add_property(const std::string& name, T* pointer, unsigned flags_ = Flags::Default) 385 | { 386 | add_property(mempool::String(name.data(), 387 | name.size(), 388 | mempool::String::allocator_type(&memory_pool_allocator)), 389 | pointer, 390 | flags_); 391 | } 392 | 393 | template 394 | void add_property(const char* name, T* pointer, unsigned flags_ = Flags::Default) 395 | { 396 | add_property(mempool::String(name, mempool::String::allocator_type(&memory_pool_allocator)), 397 | pointer, 398 | flags_); 399 | } 400 | }; 401 | 402 | template 403 | struct Converter 404 | { 405 | typedef T shadow_type; 406 | 407 | static std::unique_ptr from_shadow(const shadow_type& shadow, T& value) 408 | { 409 | (void)shadow; 410 | (void)value; 411 | return nullptr; 412 | } 413 | 414 | static void to_shadow(const T& value, shadow_type& shadow) 415 | { 416 | (void)shadow; 417 | (void)value; 418 | } 419 | 420 | static std::string type_name() { return "T"; } 421 | 422 | static constexpr bool has_specialized_type_name = false; 423 | }; 424 | 425 | template 426 | void init(T* t, ObjectHandler* h) 427 | { 428 | t->staticjson_init(h); 429 | } 430 | 431 | template 432 | class ObjectTypeHandler : public ObjectHandler 433 | { 434 | public: 435 | explicit ObjectTypeHandler(T* t) 436 | { 437 | init(t, this); 438 | postinit(); 439 | } 440 | }; 441 | 442 | template 443 | class ConversionHandler : public BaseHandler 444 | { 445 | private: 446 | typedef typename Converter::shadow_type shadow_type; 447 | typedef Handler internal_type; 448 | 449 | private: 450 | shadow_type shadow; 451 | internal_type internal; 452 | T* m_value; 453 | 454 | protected: 455 | bool postprocess(bool success) 456 | { 457 | if (!success) 458 | { 459 | return false; 460 | } 461 | if (!internal.is_parsed()) 462 | return true; 463 | this->parsed = true; 464 | auto err = Converter::from_shadow(shadow, *m_value); 465 | if (err) 466 | { 467 | this->the_error.swap(err); 468 | return false; 469 | } 470 | return true; 471 | } 472 | 473 | void reset() override 474 | { 475 | shadow = shadow_type(); 476 | internal.prepare_for_reuse(); 477 | } 478 | 479 | public: 480 | explicit ConversionHandler(T* t) : shadow(), internal(&shadow), m_value(t) {} 481 | 482 | std::string type_name() const override 483 | { 484 | // if (Converter::has_specialized_type_name) 485 | // return Converter::type_name(); 486 | return internal.type_name(); 487 | } 488 | 489 | virtual bool Null() override { return postprocess(internal.Null()); } 490 | 491 | virtual bool Bool(bool b) override { return postprocess(internal.Bool(b)); } 492 | 493 | virtual bool Int(int i) override { return postprocess(internal.Int(i)); } 494 | 495 | virtual bool Uint(unsigned u) override { return postprocess(internal.Uint(u)); } 496 | 497 | virtual bool Int64(std::int64_t i) override { return postprocess(internal.Int64(i)); } 498 | 499 | virtual bool Uint64(std::uint64_t u) override { return postprocess(internal.Uint64(u)); } 500 | 501 | virtual bool Double(double d) override { return postprocess(internal.Double(d)); } 502 | 503 | virtual bool String(const char* str, SizeType size, bool copy) override 504 | { 505 | return postprocess(internal.String(str, size, copy)); 506 | } 507 | 508 | virtual bool StartObject() override { return postprocess(internal.StartObject()); } 509 | 510 | virtual bool Key(const char* str, SizeType size, bool copy) override 511 | { 512 | return postprocess(internal.Key(str, size, copy)); 513 | } 514 | 515 | virtual bool EndObject(SizeType sz) override { return postprocess(internal.EndObject(sz)); } 516 | 517 | virtual bool StartArray() override { return postprocess(internal.StartArray()); } 518 | 519 | virtual bool EndArray(SizeType sz) override { return postprocess(internal.EndArray(sz)); } 520 | 521 | virtual bool has_error() const override 522 | { 523 | return BaseHandler::has_error() || internal.has_error(); 524 | } 525 | 526 | bool reap_error(ErrorStack& errs) override 527 | { 528 | return BaseHandler::reap_error(errs) || internal.reap_error(errs); 529 | } 530 | 531 | virtual bool write(IHandler* output) const override 532 | { 533 | Converter::to_shadow(*m_value, const_cast(shadow)); 534 | return internal.write(output); 535 | } 536 | 537 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 538 | { 539 | return internal.generate_schema(output, alloc); 540 | } 541 | }; 542 | 543 | namespace helper 544 | { 545 | template 546 | class DispatchHandler; 547 | template 548 | class DispatchHandler : public ::staticjson::ObjectTypeHandler 549 | { 550 | public: 551 | explicit DispatchHandler(T* t) : ::staticjson::ObjectTypeHandler(t) {} 552 | }; 553 | 554 | template 555 | class DispatchHandler : public ::staticjson::ConversionHandler 556 | { 557 | public: 558 | explicit DispatchHandler(T* t) : ::staticjson::ConversionHandler(t) {} 559 | }; 560 | } 561 | 562 | template 563 | class Handler 564 | : public helper::DispatchHandler::shadow_type, T>::value> 565 | { 566 | public: 567 | typedef helper::DispatchHandler::shadow_type, T>::value> 568 | base_type; 569 | explicit Handler(T* t) : base_type(t) {} 570 | }; 571 | } 572 | -------------------------------------------------------------------------------- /include/staticjson/document.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace staticjson 9 | { 10 | class JSONHandler : public BaseHandler 11 | { 12 | protected: 13 | static const int MAX_DEPTH = 32; 14 | 15 | std::vector m_stack; 16 | Value* m_value; 17 | MemoryPoolAllocator* m_alloc; 18 | 19 | private: 20 | bool stack_push(); 21 | void stack_pop(); 22 | Value& stack_top(); 23 | bool postprocess(); 24 | bool set_corrupted_dom(); 25 | 26 | public: 27 | explicit JSONHandler(Value* v, MemoryPoolAllocator* a); 28 | 29 | std::string type_name() const override; 30 | 31 | virtual bool Null() override; 32 | 33 | virtual bool Bool(bool) override; 34 | 35 | virtual bool Int(int) override; 36 | 37 | virtual bool Uint(unsigned) override; 38 | 39 | virtual bool Int64(std::int64_t) override; 40 | 41 | virtual bool Uint64(std::uint64_t) override; 42 | 43 | virtual bool Double(double) override; 44 | 45 | virtual bool String(const char*, SizeType, bool) override; 46 | 47 | virtual bool StartObject() override; 48 | 49 | virtual bool Key(const char*, SizeType, bool) override; 50 | 51 | virtual bool EndObject(SizeType) override; 52 | 53 | virtual bool StartArray() override; 54 | 55 | virtual bool EndArray(SizeType) override; 56 | 57 | virtual bool write(IHandler* output) const override; 58 | 59 | virtual void reset() override; 60 | 61 | void reset(MemoryPoolAllocator* a); 62 | 63 | void generate_schema(Value& output, MemoryPoolAllocator&) const override { output.SetObject(); } 64 | }; 65 | 66 | template <> 67 | class Handler : public JSONHandler 68 | { 69 | public: 70 | explicit Handler(Document* h) : JSONHandler(h, &h->GetAllocator()) {} 71 | virtual void reset() override 72 | { 73 | JSONHandler::reset(&(static_cast(this->m_value)->GetAllocator())); 74 | } 75 | }; 76 | 77 | namespace nonpublic 78 | { 79 | bool write_value(const Value& v, BaseHandler* out, ParseStatus* status); 80 | bool 81 | read_value(Value* v, MemoryPoolAllocator* alloc, const BaseHandler* input, ParseStatus* status); 82 | } 83 | 84 | template 85 | bool from_json_value(const Value& v, T* t, ParseStatus* status) 86 | { 87 | Handler h(t); 88 | return nonpublic::write_value(v, &h, status); 89 | } 90 | 91 | template 92 | bool from_json_document(const Document& d, 93 | T* t, 94 | ParseStatus* status) // for consistency in API 95 | { 96 | return from_json_value(d, t, status); 97 | } 98 | 99 | template 100 | bool to_json_value(Value* v, MemoryPoolAllocator* alloc, const T& t, ParseStatus* status) 101 | { 102 | Handler h(const_cast(&t)); 103 | return nonpublic::read_value(v, alloc, &h, status); 104 | } 105 | 106 | template 107 | bool to_json_document(Document* d, const T& t, ParseStatus* status) 108 | { 109 | return to_json_value(d, &d->GetAllocator(), t, status); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /include/staticjson/enum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace staticjson 9 | { 10 | template 11 | class EnumHandler : public BaseHandler 12 | { 13 | private: 14 | Enum* m_value; 15 | 16 | static const std::vector>& get_mapping() 17 | { 18 | return Derived::get_mapping(); 19 | }; 20 | 21 | public: 22 | explicit EnumHandler(Enum* value) : m_value(value) {} 23 | 24 | bool String(const char* str, SizeType sz, bool) override 25 | { 26 | const auto& mapping = get_mapping(); 27 | for (const std::pair& pair : mapping) 28 | { 29 | if (pair.first.size() == sz && memcmp(pair.first.data(), str, sz) == 0) 30 | { 31 | *m_value = pair.second; 32 | this->parsed = true; 33 | return true; 34 | } 35 | } 36 | the_error.reset(new error::InvalidEnumError(std::string(str, str + sz))); 37 | return false; 38 | } 39 | 40 | bool write(IHandler* output) const override 41 | { 42 | const auto& mapping = get_mapping(); 43 | for (const std::pair& pair : mapping) 44 | { 45 | if (*m_value == pair.second) 46 | { 47 | output->String(pair.first.data(), static_cast(pair.first.size()), false); 48 | return true; 49 | } 50 | } 51 | return false; 52 | } 53 | 54 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 55 | { 56 | output.SetObject(); 57 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("string"), alloc); 58 | Value enumerations(rapidjson::kArrayType); 59 | const auto& mapping = get_mapping(); 60 | for (const std::pair& pair : mapping) 61 | { 62 | enumerations.PushBack(rapidjson::StringRef(pair.first.data(), pair.first.size()), 63 | alloc); 64 | } 65 | output.AddMember(rapidjson::StringRef("enum"), enumerations, alloc); 66 | } 67 | }; 68 | } 69 | 70 | #define STATICJSON_DECLARE_ENUM(type, ...) \ 71 | namespace staticjson \ 72 | { \ 73 | template <> \ 74 | class Handler : public EnumHandler> \ 75 | { \ 76 | public: \ 77 | explicit Handler(type* value) : EnumHandler>(value) {} \ 78 | std::string type_name() const override { return #type; } \ 79 | static const std::vector>& get_mapping() \ 80 | { \ 81 | static std::vector> mapping{__VA_ARGS__}; \ 82 | return mapping; \ 83 | } \ 84 | }; \ 85 | } 86 | -------------------------------------------------------------------------------- /include/staticjson/error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace staticjson 11 | { 12 | std::string quote(const std::string& str); 13 | 14 | class ErrorStack; 15 | class ErrorBase; 16 | 17 | namespace error 18 | { 19 | namespace internal 20 | { 21 | class error_stack_const_iterator; 22 | } 23 | using staticjson::ErrorBase; 24 | using staticjson::ErrorStack; 25 | } 26 | 27 | class ErrorBase 28 | { 29 | protected: 30 | explicit ErrorBase() : next(0) {} 31 | 32 | private: 33 | ErrorBase* next; 34 | 35 | friend class ErrorStack; 36 | friend class error::internal::error_stack_const_iterator; 37 | 38 | public: 39 | virtual int type() const = 0; 40 | virtual bool is_intermediate() const { return false; } 41 | virtual ~ErrorBase() {} 42 | virtual std::string description() const = 0; 43 | }; 44 | 45 | namespace error 46 | { 47 | typedef int error_type; 48 | 49 | static const error_type SUCCESS = 0, OBJECT_MEMBER = 1, ARRAY_ELEMENT = 2, MISSING_REQUIRED = 3, 50 | TYPE_MISMATCH = 4, NUMBER_OUT_OF_RANGE = 5, ARRAY_LENGTH_MISMATCH = 6, 51 | UNKNOWN_FIELD = 7, DUPLICATE_KEYS = 8, CORRUPTED_DOM = 9, 52 | TOO_DEEP_RECURSION = 10, INVALID_ENUM = 11, TOO_MANY_LEAVES = 12, 53 | CUSTOM = -1; 54 | 55 | class Success : public ErrorBase 56 | { 57 | public: 58 | explicit Success() {} 59 | std::string description() const; 60 | error_type type() const { return SUCCESS; } 61 | }; 62 | 63 | class IntermediateError : public ErrorBase 64 | { 65 | public: 66 | bool is_intermediate() const { return true; } 67 | }; 68 | 69 | class ObjectMemberError : public IntermediateError 70 | { 71 | private: 72 | std::string m_member_name; 73 | 74 | public: 75 | explicit ObjectMemberError(std::string memberName) { m_member_name.swap(memberName); } 76 | 77 | const std::string& member_name() const { return m_member_name; } 78 | 79 | std::string description() const; 80 | 81 | error_type type() const { return OBJECT_MEMBER; } 82 | }; 83 | 84 | class ArrayElementError : public IntermediateError 85 | { 86 | private: 87 | std::size_t m_index; 88 | 89 | public: 90 | explicit ArrayElementError(std::size_t idx) : m_index(idx) {} 91 | 92 | std::size_t index() const { return m_index; } 93 | 94 | std::string description() const; 95 | 96 | error_type type() const { return ARRAY_ELEMENT; } 97 | }; 98 | 99 | class RequiredFieldMissingError : public ErrorBase 100 | { 101 | private: 102 | std::vector m_missing_members; 103 | 104 | public: 105 | explicit RequiredFieldMissingError() {} 106 | 107 | std::vector& missing_members() { return m_missing_members; }; 108 | 109 | const std::vector& missing_members() const { return m_missing_members; } 110 | 111 | std::string description() const; 112 | 113 | error_type type() const { return MISSING_REQUIRED; } 114 | }; 115 | 116 | class TypeMismatchError : public ErrorBase 117 | { 118 | private: 119 | std::string m_expected_type; 120 | std::string m_actual_type; 121 | 122 | public: 123 | explicit TypeMismatchError(std::string expectedType, std::string actualType) 124 | { 125 | m_expected_type.swap(expectedType); 126 | m_actual_type.swap(actualType); 127 | } 128 | 129 | const std::string& expected_type() const { return m_expected_type; } 130 | 131 | const std::string& actual_type() const { return m_actual_type; } 132 | 133 | std::string description() const; 134 | 135 | error_type type() const { return TYPE_MISMATCH; } 136 | }; 137 | 138 | class RecursionTooDeepError : public ErrorBase 139 | { 140 | std::string description() const override; 141 | error_type type() const override { return TOO_DEEP_RECURSION; } 142 | }; 143 | class TooManyLeavesError : public ErrorBase 144 | { 145 | std::string description() const override; 146 | error_type type() const override { return TOO_MANY_LEAVES; } 147 | }; 148 | class NumberOutOfRangeError : public ErrorBase 149 | { 150 | std::string m_expected_type; 151 | std::string m_actual_type; 152 | 153 | public: 154 | explicit NumberOutOfRangeError(std::string expectedType, std::string actualType) 155 | { 156 | m_expected_type.swap(expectedType); 157 | m_actual_type.swap(actualType); 158 | } 159 | 160 | const std::string& expected_type() const { return m_expected_type; } 161 | 162 | const std::string& actual_type() const { return m_actual_type; } 163 | 164 | std::string description() const; 165 | 166 | error_type type() const { return NUMBER_OUT_OF_RANGE; } 167 | }; 168 | 169 | class DuplicateKeyError : public ErrorBase 170 | { 171 | private: 172 | std::string key_name; 173 | 174 | public: 175 | explicit DuplicateKeyError(std::string name) { key_name.swap(name); } 176 | 177 | const std::string& key() const { return key_name; } 178 | 179 | error_type type() const { return DUPLICATE_KEYS; } 180 | 181 | std::string description() const; 182 | }; 183 | 184 | class UnknownFieldError : public ErrorBase 185 | { 186 | private: 187 | std::string m_name; 188 | 189 | public: 190 | explicit UnknownFieldError(const char* name, std::size_t length) : m_name(name, length) {} 191 | 192 | const std::string& field_name() const { return m_name; } 193 | 194 | error_type type() const { return UNKNOWN_FIELD; } 195 | 196 | std::string description() const; 197 | }; 198 | 199 | class CorruptedDOMError : public ErrorBase 200 | { 201 | public: 202 | std::string description() const; 203 | 204 | error_type type() const { return CORRUPTED_DOM; } 205 | }; 206 | 207 | class ArrayLengthMismatchError : public ErrorBase 208 | { 209 | public: 210 | std::string description() const; 211 | 212 | error_type type() const { return ARRAY_LENGTH_MISMATCH; } 213 | }; 214 | 215 | class InvalidEnumError : public ErrorBase 216 | { 217 | private: 218 | std::string m_name; 219 | 220 | public: 221 | explicit InvalidEnumError(std::string name) { m_name.swap(name); } 222 | std::string description() const; 223 | error_type type() const { return INVALID_ENUM; } 224 | }; 225 | 226 | class CustomError : public ErrorBase 227 | { 228 | private: 229 | std::string m_message; 230 | 231 | public: 232 | explicit CustomError(std::string message) { m_message.swap(message); } 233 | std::string description() const; 234 | error_type type() const { return CUSTOM; } 235 | }; 236 | 237 | namespace internal 238 | { 239 | 240 | class error_stack_const_iterator 241 | { 242 | private: 243 | const ErrorBase* e; 244 | 245 | public: 246 | using iterator_category = std::forward_iterator_tag; 247 | using value_type = const ErrorBase; 248 | using difference_type = ptrdiff_t; 249 | using pointer = const ErrorBase*; 250 | using reference = const ErrorBase&; 251 | 252 | public: 253 | explicit error_stack_const_iterator(const ErrorBase* p) : e(p) {} 254 | reference operator*() const { return *e; } 255 | 256 | pointer operator->() const { return e; } 257 | 258 | error_stack_const_iterator& operator++() 259 | { 260 | e = e->next; 261 | return *this; 262 | } 263 | 264 | bool operator==(error_stack_const_iterator that) const { return e == that.e; } 265 | 266 | bool operator!=(error_stack_const_iterator that) const { return e != that.e; } 267 | }; 268 | } 269 | } 270 | 271 | class ErrorStack 272 | { 273 | private: 274 | ErrorBase* head; 275 | std::size_t m_size; 276 | 277 | ErrorStack(const ErrorStack&); 278 | ErrorStack& operator=(const ErrorStack&); 279 | 280 | public: 281 | typedef error::internal::error_stack_const_iterator const_iterator; 282 | 283 | explicit ErrorStack() : head(0), m_size(0) {} 284 | 285 | const_iterator begin() const { return const_iterator(head); } 286 | 287 | const_iterator end() const { return const_iterator(0); } 288 | 289 | // This will take the ownership of e 290 | // Requires it to be dynamically allocated 291 | void push(ErrorBase* e) 292 | { 293 | if (e) 294 | { 295 | e->next = head; 296 | head = e; 297 | ++m_size; 298 | } 299 | } 300 | 301 | // The caller will take the responsibility of deleting the returned pointer 302 | // Returns NULL when empty 303 | ErrorBase* pop() 304 | { 305 | if (head) 306 | { 307 | ErrorBase* result = head; 308 | head = head->next; 309 | --m_size; 310 | return result; 311 | } 312 | return 0; 313 | } 314 | 315 | bool empty() const { return head == 0; } 316 | 317 | explicit operator bool() const { return !empty(); } 318 | 319 | bool operator!() const { return empty(); } 320 | 321 | std::size_t size() const { return m_size; } 322 | 323 | ~ErrorStack() 324 | { 325 | while (head) 326 | { 327 | ErrorBase* next = head->next; 328 | delete head; 329 | head = next; 330 | } 331 | } 332 | 333 | void swap(ErrorStack& other) noexcept 334 | { 335 | std::swap(head, other.head); 336 | std::swap(m_size, other.m_size); 337 | } 338 | 339 | ErrorStack(ErrorStack&& other) : head(other.head), m_size(other.m_size) 340 | { 341 | other.head = 0; 342 | other.m_size = 0; 343 | } 344 | 345 | ErrorStack& operator==(ErrorStack&& other) 346 | { 347 | swap(other); 348 | return *this; 349 | } 350 | }; 351 | 352 | // For argument dependent lookup 353 | inline void swap(ErrorStack& s1, ErrorStack& s2) { s1.swap(s2); } 354 | 355 | class ParseStatus 356 | { 357 | private: 358 | ErrorStack m_stack; 359 | std::size_t m_offset; 360 | int m_code; 361 | 362 | public: 363 | explicit ParseStatus() : m_stack(), m_offset(), m_code() {} 364 | 365 | void set_result(int err, std::size_t off) 366 | { 367 | m_code = err; 368 | m_offset = off; 369 | } 370 | 371 | int error_code() const { return m_code; } 372 | 373 | std::size_t offset() const { return m_offset; } 374 | 375 | std::string short_description() const; 376 | 377 | ErrorStack& error_stack() { return m_stack; } 378 | 379 | const ErrorStack& error_stack() const { return m_stack; } 380 | 381 | typedef ErrorStack::const_iterator const_iterator; 382 | 383 | const_iterator begin() const { return m_stack.begin(); } 384 | 385 | const_iterator end() const { return m_stack.end(); } 386 | 387 | bool has_error() const { return m_code != 0 || !m_stack.empty(); } 388 | 389 | std::string description() const; 390 | 391 | void swap(ParseStatus& other) noexcept 392 | { 393 | std::swap(m_code, other.m_code); 394 | std::swap(m_offset, other.m_offset); 395 | m_stack.swap(other.m_stack); 396 | } 397 | 398 | bool operator!() const { return has_error(); } 399 | 400 | explicit operator bool() const { return !has_error(); } 401 | 402 | ParseStatus(ParseStatus&& other) noexcept : m_stack(), m_offset(), m_code() { swap(other); } 403 | 404 | ParseStatus& operator==(ParseStatus&& other) noexcept 405 | { 406 | swap(other); 407 | return *this; 408 | } 409 | }; 410 | 411 | // For argument dependent lookup 412 | inline void swap(ParseStatus& r1, ParseStatus& r2) { r1.swap(r2); } 413 | } 414 | -------------------------------------------------------------------------------- /include/staticjson/forward_declarations.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace staticjson 4 | { 5 | typedef unsigned int SizeType; 6 | 7 | class IHandler; 8 | 9 | class BaseHandler; 10 | 11 | class ObjectHandler; 12 | 13 | template 14 | class Handler; 15 | } 16 | -------------------------------------------------------------------------------- /include/staticjson/io.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace staticjson 9 | { 10 | 11 | namespace nonpublic 12 | { 13 | bool parse_json_string(const char* str, BaseHandler* handler, ParseStatus* status); 14 | bool parse_json_file(std::FILE* fp, BaseHandler* handler, ParseStatus* status); 15 | std::string serialize_json_string(const BaseHandler* handler); 16 | bool serialize_json_file(std::FILE* fp, const BaseHandler* handler); 17 | std::string serialize_pretty_json_string(const BaseHandler* handler); 18 | bool serialize_pretty_json_file(std::FILE* fp, const BaseHandler* handler); 19 | 20 | struct FileGuard : private NonMobile 21 | { 22 | std::FILE* fp; 23 | 24 | explicit FileGuard(std::FILE* fp) : fp(fp) {} 25 | ~FileGuard() 26 | { 27 | if (fp) 28 | std::fclose(fp); 29 | } 30 | }; 31 | } 32 | 33 | template 34 | inline bool from_json_string(const char* str, T* value, ParseStatus* status) 35 | { 36 | Handler h(value); 37 | return nonpublic::parse_json_string(str, &h, status); 38 | } 39 | 40 | template 41 | inline bool from_json_file(std::FILE* fp, T* value, ParseStatus* status) 42 | { 43 | Handler h(value); 44 | return nonpublic::parse_json_file(fp, &h, status); 45 | } 46 | 47 | template 48 | inline bool from_json_file(const char* filename, T* value, ParseStatus* status) 49 | { 50 | nonpublic::FileGuard fg(std::fopen(filename, "r")); 51 | return from_json_file(fg.fp, value, status); 52 | } 53 | 54 | template 55 | inline bool from_json_file(const std::string& filename, T* value, ParseStatus* status) 56 | { 57 | return from_json_file(filename.c_str(), value, status); 58 | } 59 | 60 | template 61 | inline std::string to_json_string(const T& value) 62 | { 63 | Handler h(const_cast(&value)); 64 | return nonpublic::serialize_json_string(&h); 65 | } 66 | 67 | template 68 | inline bool to_json_file(std::FILE* fp, const T& value) 69 | { 70 | Handler h(const_cast(&value)); 71 | return nonpublic::serialize_json_file(fp, &h); 72 | } 73 | 74 | template 75 | inline bool to_json_file(const char* filename, const T& value) 76 | { 77 | nonpublic::FileGuard fg(std::fopen(filename, "wb")); 78 | return to_json_file(fg.fp, value); 79 | } 80 | 81 | template 82 | inline bool to_json_file(const std::string& filename, const T& value) 83 | { 84 | return to_json_file(filename.c_str(), value); 85 | } 86 | 87 | template 88 | inline std::string to_pretty_json_string(const T& value) 89 | { 90 | Handler h(const_cast(&value)); 91 | return nonpublic::serialize_pretty_json_string(&h); 92 | } 93 | 94 | template 95 | inline bool to_pretty_json_file(std::FILE* fp, const T& value) 96 | { 97 | Handler h(const_cast(&value)); 98 | return nonpublic::serialize_pretty_json_file(fp, &h); 99 | } 100 | 101 | template 102 | inline bool to_pretty_json_file(const char* filename, const T& value) 103 | { 104 | nonpublic::FileGuard fg(std::fopen(filename, "wb")); 105 | return to_pretty_json_file(fg.fp, value); 106 | } 107 | 108 | template 109 | inline bool to_pretty_json_file(const std::string& filename, const T& value) 110 | { 111 | return to_pretty_json_file(filename.c_str(), value); 112 | } 113 | 114 | template 115 | inline Document export_json_schema(T* value, Document::AllocatorType* allocator = nullptr) 116 | { 117 | Handler h(value); 118 | Document d; 119 | h.generate_schema(d, allocator ? *allocator : d.GetAllocator()); 120 | return d; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /include/staticjson/optional_support.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stl_types.hpp" 4 | 5 | #ifdef __has_include 6 | #if __has_include() 7 | #include 8 | 9 | namespace staticjson 10 | { 11 | template 12 | using optional = std::optional; 13 | 14 | using std::nullopt; 15 | } 16 | 17 | #elif __has_include() 18 | #include 19 | 20 | namespace staticjson 21 | { 22 | template 23 | using optional = std::experimental::optional; 24 | 25 | using std::experimental::nullopt; 26 | } 27 | 28 | #else 29 | #error "Missing " 30 | #endif 31 | #else 32 | #error "Missing " 33 | #endif 34 | 35 | namespace staticjson 36 | { 37 | 38 | template 39 | class Handler> : public BaseHandler 40 | { 41 | public: 42 | using ElementType = T; 43 | 44 | protected: 45 | mutable optional* m_value; 46 | mutable optional> internal_handler; 47 | int depth = 0; 48 | 49 | public: 50 | explicit Handler(optional* value) : m_value(value) {} 51 | 52 | protected: 53 | void initialize() 54 | { 55 | if (!internal_handler) 56 | { 57 | m_value->emplace(); 58 | internal_handler.emplace(&(**m_value)); 59 | } 60 | } 61 | 62 | void reset() override 63 | { 64 | depth = 0; 65 | internal_handler = nullopt; 66 | *m_value = nullopt; 67 | } 68 | 69 | bool postcheck(bool success) 70 | { 71 | if (success) 72 | this->parsed = internal_handler->is_parsed(); 73 | return success; 74 | } 75 | 76 | public: 77 | bool Null() override 78 | { 79 | if (depth == 0) 80 | { 81 | *m_value = nullopt; 82 | this->parsed = true; 83 | return true; 84 | } 85 | else 86 | { 87 | initialize(); 88 | return postcheck(internal_handler->Null()); 89 | } 90 | } 91 | 92 | bool write(IHandler* out) const override 93 | { 94 | if (!m_value || !(*m_value)) 95 | { 96 | return out->Null(); 97 | } 98 | if (!internal_handler) 99 | { 100 | internal_handler.emplace(&(**m_value)); 101 | } 102 | return internal_handler->write(out); 103 | } 104 | 105 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 106 | { 107 | const_cast>*>(this)->initialize(); 108 | output.SetObject(); 109 | Value anyOf(rapidjson::kArrayType); 110 | Value nullDescriptor(rapidjson::kObjectType); 111 | nullDescriptor.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("null"), alloc); 112 | Value descriptor; 113 | internal_handler->generate_schema(descriptor, alloc); 114 | anyOf.PushBack(nullDescriptor, alloc); 115 | anyOf.PushBack(descriptor, alloc); 116 | output.AddMember(rapidjson::StringRef("anyOf"), anyOf, alloc); 117 | } 118 | 119 | bool Bool(bool b) override 120 | { 121 | initialize(); 122 | return postcheck(internal_handler->Bool(b)); 123 | } 124 | 125 | bool Int(int i) override 126 | { 127 | initialize(); 128 | return postcheck(internal_handler->Int(i)); 129 | } 130 | 131 | bool Uint(unsigned i) override 132 | { 133 | initialize(); 134 | return postcheck(internal_handler->Uint(i)); 135 | } 136 | 137 | bool Int64(std::int64_t i) override 138 | { 139 | initialize(); 140 | return postcheck(internal_handler->Int64(i)); 141 | } 142 | 143 | bool Uint64(std::uint64_t i) override 144 | { 145 | initialize(); 146 | return postcheck(internal_handler->Uint64(i)); 147 | } 148 | 149 | bool Double(double i) override 150 | { 151 | initialize(); 152 | return postcheck(internal_handler->Double(i)); 153 | } 154 | 155 | bool String(const char* str, SizeType len, bool copy) override 156 | { 157 | initialize(); 158 | return postcheck(internal_handler->String(str, len, copy)); 159 | } 160 | 161 | bool Key(const char* str, SizeType len, bool copy) override 162 | { 163 | initialize(); 164 | return postcheck(internal_handler->Key(str, len, copy)); 165 | } 166 | 167 | bool StartObject() override 168 | { 169 | initialize(); 170 | ++depth; 171 | return internal_handler->StartObject(); 172 | } 173 | 174 | bool EndObject(SizeType len) override 175 | { 176 | initialize(); 177 | --depth; 178 | return postcheck(internal_handler->EndObject(len)); 179 | } 180 | 181 | bool StartArray() override 182 | { 183 | initialize(); 184 | ++depth; 185 | return postcheck(internal_handler->StartArray()); 186 | } 187 | 188 | bool EndArray(SizeType len) override 189 | { 190 | initialize(); 191 | --depth; 192 | return postcheck(internal_handler->EndArray(len)); 193 | } 194 | 195 | bool has_error() const override { return internal_handler && internal_handler->has_error(); } 196 | 197 | bool reap_error(ErrorStack& stk) override 198 | { 199 | return internal_handler && internal_handler->reap_error(stk); 200 | } 201 | 202 | std::string type_name() const override 203 | { 204 | if (this->internal_handler) 205 | { 206 | return "std::optional<" + this->internal_handler->type_name() + ">"; 207 | } 208 | return "std::optional"; 209 | } 210 | }; 211 | } 212 | -------------------------------------------------------------------------------- /include/staticjson/primitive_types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace staticjson 9 | { 10 | 11 | template 12 | class IntegerHandler : public BaseHandler 13 | { 14 | static_assert(std::is_arithmetic::value, "Only arithmetic types are allowed"); 15 | 16 | protected: 17 | IntType* m_value; 18 | 19 | template 20 | static constexpr typename std::enable_if::value, bool>::type 21 | is_out_of_range(AnotherIntType a) 22 | { 23 | typedef typename std::common_type::type CommonType; 24 | typedef typename std::numeric_limits this_limits; 25 | typedef typename std::numeric_limits that_limits; 26 | 27 | // The extra logic related to this_limits::min/max allows the compiler to 28 | // short circuit this check at compile time. For instance, a `uint32_t` 29 | // will NEVER be out of range for an `int64_t` 30 | return ((this_limits::is_signed == that_limits::is_signed) 31 | ? ((CommonType(this_limits::min()) > CommonType(a) 32 | || CommonType(this_limits::max()) < CommonType(a))) 33 | : (this_limits::is_signed) 34 | ? (CommonType(this_limits::max()) < CommonType(a)) 35 | : (a < 0 || CommonType(a) > CommonType(this_limits::max()))); 36 | } 37 | 38 | template 39 | static constexpr typename std::enable_if::value, bool>::type 40 | is_out_of_range(FloatType f) 41 | { 42 | return static_cast(static_cast(f)) != f; 43 | } 44 | 45 | template 46 | bool receive(ReceiveNumType r, const char* actual_type) 47 | { 48 | if (is_out_of_range(r)) 49 | return set_out_of_range(actual_type); 50 | *m_value = static_cast(r); 51 | this->parsed = true; 52 | return true; 53 | } 54 | 55 | public: 56 | explicit IntegerHandler(IntType* value) : m_value(value) {} 57 | 58 | bool Int(int i) override { return receive(i, "int"); } 59 | 60 | bool Uint(unsigned i) override { return receive(i, "unsigned int"); } 61 | 62 | bool Int64(std::int64_t i) override { return receive(i, "std::int64_t"); } 63 | 64 | bool Uint64(std::uint64_t i) override { return receive(i, "std::uint64_t"); } 65 | 66 | bool Double(double d) override { return receive(d, "double"); } 67 | 68 | bool write(IHandler* output) const override 69 | { 70 | if (std::numeric_limits::is_signed) 71 | { 72 | return output->Int64(*m_value); 73 | } 74 | else 75 | { 76 | return output->Uint64(*m_value); 77 | } 78 | } 79 | 80 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 81 | { 82 | output.SetObject(); 83 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("integer"), alloc); 84 | Value minimum, maximum; 85 | if (std::numeric_limits::is_signed) 86 | { 87 | minimum.SetInt64(std::numeric_limits::min()); 88 | maximum.SetInt64(std::numeric_limits::max()); 89 | } 90 | else 91 | { 92 | minimum.SetUint64(std::numeric_limits::min()); 93 | maximum.SetUint64(std::numeric_limits::max()); 94 | } 95 | output.AddMember(rapidjson::StringRef("minimum"), minimum, alloc); 96 | output.AddMember(rapidjson::StringRef("maximum"), maximum, alloc); 97 | } 98 | }; 99 | 100 | template <> 101 | class Handler : public BaseHandler 102 | { 103 | public: 104 | explicit Handler(std::nullptr_t*) {} 105 | 106 | bool Null() override 107 | { 108 | this->parsed = true; 109 | return true; 110 | } 111 | 112 | std::string type_name() const override { return "null"; } 113 | 114 | bool write(IHandler* output) const override { return output->Null(); } 115 | 116 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 117 | { 118 | output.SetObject(); 119 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("null"), alloc); 120 | } 121 | }; 122 | 123 | template <> 124 | class Handler : public BaseHandler 125 | { 126 | private: 127 | bool* m_value; 128 | 129 | public: 130 | explicit Handler(bool* value) : m_value(value) {} 131 | 132 | bool Bool(bool v) override 133 | { 134 | *m_value = v; 135 | this->parsed = true; 136 | return true; 137 | } 138 | 139 | std::string type_name() const override { return "bool"; } 140 | 141 | bool write(IHandler* output) const override { return output->Bool(*m_value); } 142 | 143 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 144 | { 145 | output.SetObject(); 146 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc); 147 | } 148 | }; 149 | 150 | template <> 151 | class Handler : public IntegerHandler 152 | { 153 | public: 154 | explicit Handler(int* i) : IntegerHandler(i) {} 155 | 156 | std::string type_name() const override { return "int"; } 157 | 158 | bool write(IHandler* output) const override { return output->Int(*m_value); } 159 | }; 160 | 161 | template <> 162 | class Handler : public IntegerHandler 163 | { 164 | public: 165 | explicit Handler(unsigned* i) : IntegerHandler(i) {} 166 | 167 | std::string type_name() const override { return "unsigned int"; } 168 | 169 | bool write(IHandler* output) const override { return output->Uint(*m_value); } 170 | }; 171 | 172 | template <> 173 | class Handler : public IntegerHandler 174 | { 175 | public: 176 | explicit Handler(long* i) : IntegerHandler(i) {} 177 | 178 | std::string type_name() const override { return "long"; } 179 | }; 180 | 181 | template <> 182 | class Handler : public IntegerHandler 183 | { 184 | public: 185 | explicit Handler(unsigned long* i) : IntegerHandler(i) {} 186 | 187 | std::string type_name() const override { return "unsigned long"; } 188 | }; 189 | 190 | template <> 191 | class Handler : public IntegerHandler 192 | { 193 | public: 194 | explicit Handler(long long* i) : IntegerHandler(i) {} 195 | 196 | std::string type_name() const override { return "long long"; } 197 | }; 198 | 199 | template <> 200 | class Handler : public IntegerHandler 201 | { 202 | public: 203 | explicit Handler(unsigned long long* i) : IntegerHandler(i) {} 204 | 205 | std::string type_name() const override { return "unsigned long long"; } 206 | }; 207 | 208 | // char is an alias for bool to work around the stupid `std::vector` 209 | template <> 210 | class Handler : public BaseHandler 211 | { 212 | private: 213 | char* m_value; 214 | 215 | public: 216 | explicit Handler(char* i) : m_value(i) {} 217 | 218 | std::string type_name() const override { return "bool"; } 219 | 220 | bool Bool(bool v) override 221 | { 222 | *this->m_value = v; 223 | this->parsed = true; 224 | return true; 225 | } 226 | 227 | bool write(IHandler* out) const override { return out->Bool(*m_value != 0); } 228 | 229 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 230 | { 231 | output.SetObject(); 232 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc); 233 | } 234 | }; 235 | 236 | template <> 237 | class Handler : public BaseHandler 238 | { 239 | private: 240 | double* m_value; 241 | 242 | public: 243 | explicit Handler(double* v) : m_value(v) {} 244 | 245 | bool Int(int i) override 246 | { 247 | *m_value = i; 248 | this->parsed = true; 249 | return true; 250 | } 251 | 252 | bool Uint(unsigned i) override 253 | { 254 | *m_value = i; 255 | this->parsed = true; 256 | return true; 257 | } 258 | 259 | bool Int64(std::int64_t i) override 260 | { 261 | *m_value = static_cast(i); 262 | if (static_cast(*m_value) != i) 263 | return set_out_of_range("std::int64_t"); 264 | this->parsed = true; 265 | return true; 266 | } 267 | 268 | bool Uint64(std::uint64_t i) override 269 | { 270 | *m_value = static_cast(i); 271 | if (static_cast(*m_value) != i) 272 | return set_out_of_range("std::uint64_t"); 273 | this->parsed = true; 274 | return true; 275 | } 276 | 277 | bool Double(double d) override 278 | { 279 | *m_value = d; 280 | this->parsed = true; 281 | return true; 282 | } 283 | 284 | std::string type_name() const override { return "double"; } 285 | 286 | bool write(IHandler* out) const override { return out->Double(*m_value); } 287 | 288 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 289 | { 290 | output.SetObject(); 291 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc); 292 | } 293 | }; 294 | 295 | template <> 296 | class Handler : public BaseHandler 297 | { 298 | private: 299 | float* m_value; 300 | 301 | public: 302 | explicit Handler(float* v) : m_value(v) {} 303 | 304 | bool Int(int i) override 305 | { 306 | *m_value = static_cast(i); 307 | if (static_cast(*m_value) != i) 308 | return set_out_of_range("int"); 309 | this->parsed = true; 310 | return true; 311 | } 312 | 313 | bool Uint(unsigned i) override 314 | { 315 | *m_value = static_cast(i); 316 | if (static_cast(*m_value) != i) 317 | return set_out_of_range("unsigned int"); 318 | this->parsed = true; 319 | return true; 320 | } 321 | 322 | bool Int64(std::int64_t i) override 323 | { 324 | *m_value = static_cast(i); 325 | if (static_cast(*m_value) != i) 326 | return set_out_of_range("std::int64_t"); 327 | this->parsed = true; 328 | return true; 329 | } 330 | 331 | bool Uint64(std::uint64_t i) override 332 | { 333 | *m_value = static_cast(i); 334 | if (static_cast(*m_value) != i) 335 | return set_out_of_range("std::uint64_t"); 336 | this->parsed = true; 337 | return true; 338 | } 339 | 340 | bool Double(double d) override 341 | { 342 | *m_value = static_cast(d); 343 | this->parsed = true; 344 | return true; 345 | } 346 | 347 | std::string type_name() const override { return "float"; } 348 | 349 | bool write(IHandler* out) const override { return out->Double(*m_value); } 350 | 351 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 352 | { 353 | output.SetObject(); 354 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc); 355 | } 356 | }; 357 | 358 | template <> 359 | class Handler : public BaseHandler 360 | { 361 | private: 362 | std::string* m_value; 363 | 364 | public: 365 | explicit Handler(std::string* v) : m_value(v) {} 366 | 367 | bool String(const char* str, SizeType length, bool) override 368 | { 369 | m_value->assign(str, length); 370 | this->parsed = true; 371 | return true; 372 | } 373 | 374 | std::string type_name() const override { return "string"; } 375 | 376 | bool write(IHandler* out) const override 377 | { 378 | return out->String(m_value->data(), SizeType(m_value->size()), true); 379 | } 380 | 381 | void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override 382 | { 383 | output.SetObject(); 384 | output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("string"), alloc); 385 | } 386 | }; 387 | } 388 | -------------------------------------------------------------------------------- /include/staticjson/staticjson.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | -------------------------------------------------------------------------------- /test/myarray.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // This class is used to test custom conversion functions in StaticJSON. 5 | 6 | template 7 | class Array 8 | { 9 | private: 10 | T* m_data; 11 | size_t m_size; 12 | 13 | public: 14 | explicit Array() : m_data(nullptr), m_size(0) {} 15 | explicit Array(size_t size) : m_size(size) { m_data = new T[size]; } 16 | ~Array() { clear(); } 17 | Array(Array&& that) noexcept 18 | { 19 | m_data = that.m_data; 20 | m_size = that.m_size; 21 | that.m_data = nullptr; 22 | that.m_size = 0; 23 | } 24 | Array& operator=(Array&& that) noexcept 25 | { 26 | std::swap(m_data, that.m_data); 27 | std::swap(m_size, that.m_size); 28 | return *this; 29 | } 30 | const T& operator[](size_t i) const { return m_data[i]; } 31 | T& operator[](size_t i) { return m_data[i]; } 32 | size_t size() const { return m_size; } 33 | const T& back() const { return m_data[m_size - 1]; } 34 | T& back() { return m_data[m_size - 1]; } 35 | const T& front() const { return m_data[0]; } 36 | T& front() { return m_data[0]; } 37 | bool empty() const { return m_size == 0; } 38 | void clear() 39 | { 40 | delete[] m_data; 41 | m_data = nullptr; 42 | m_size = 0; 43 | } 44 | }; 45 | 46 | namespace staticjson 47 | { 48 | template 49 | struct Converter> 50 | { 51 | typedef std::deque shadow_type; 52 | 53 | static std::unique_ptr from_shadow(const shadow_type& shadow, Array& value) 54 | { 55 | value = Array(shadow.size()); 56 | for (size_t i = 0; i < shadow.size(); ++i) 57 | { 58 | value[i] = shadow[i]; 59 | } 60 | return nullptr; 61 | } 62 | 63 | static void to_shadow(const Array& value, shadow_type& shadow) 64 | { 65 | shadow.resize(value.size()); 66 | for (size_t i = 0; i < shadow.size(); ++i) 67 | { 68 | shadow[i] = value[i]; 69 | } 70 | } 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /test/test_autojsoncxx.cpp: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Siyuan Ren (netheril96@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "catch.hpp" 24 | 25 | #define AUTOJSONCXX_ROOT_DIRECTORY get_base_dir() + "/autojsoncxx/" + 26 | 27 | #include 28 | 29 | #include "userdef.hpp" 30 | 31 | #include 32 | #include 33 | 34 | using namespace autojsoncxx; 35 | using namespace config; 36 | using namespace config::event; 37 | 38 | const std::string& get_base_dir(void); 39 | 40 | namespace config 41 | { 42 | inline bool operator==(Date d1, Date d2) 43 | { 44 | return d1.year == d2.year && d1.month == d2.month && d1.day == d2.day; 45 | } 46 | 47 | inline bool operator!=(Date d1, Date d2) { return !(d1 == d2); } 48 | } 49 | 50 | namespace config 51 | { 52 | namespace event 53 | { 54 | bool operator==(const BlockEvent& b1, const BlockEvent& b2) 55 | { 56 | return b1.admin_ID == b2.admin_ID && b1.date == b2.date && b1.description == b2.description 57 | && b1.details == b2.details && b1.serial_number == b2.serial_number; 58 | } 59 | } 60 | 61 | bool operator==(const User& u1, const User& u2) 62 | { 63 | return u1.birthday == u2.birthday 64 | && (u1.block_event == u2.block_event 65 | || (u1.block_event && u2.block_event && *u1.block_event == *u2.block_event)) 66 | && u1.dark_history == u2.dark_history && u1.ID == u2.ID && u1.nickname == u2.nickname 67 | && u1.optional_attributes == u2.optional_attributes; 68 | } 69 | } 70 | 71 | static std::string read_all(const std::string& file_name) 72 | { 73 | std::ifstream input(file_name.c_str()); 74 | std::ostringstream buffer; 75 | buffer << input.rdbuf(); 76 | return buffer.str(); 77 | } 78 | 79 | static Date create_date(int year, int month, int day) 80 | { 81 | Date d; 82 | d.year = year; 83 | d.month = month; 84 | d.day = day; 85 | return d; 86 | } 87 | 88 | // If most of the cases fail, you probably set the work directory wrong. 89 | // Point the work directory to the `test/` subdirectory 90 | // or redefine the macro AUTOJSONCXX_ROOT_DIRECTORY. 91 | 92 | TEST_CASE("Test for the constructor of generated class (old)", "[code generator]") 93 | { 94 | User user; 95 | 96 | REQUIRE(user.ID == 0ULL); 97 | 98 | // Do not use string literal because MSVC will mess up the encoding 99 | static const char default_nickname[] = {char(0xe2), 100 | char(0x9d), 101 | char(0xb6), 102 | char(0xe2), 103 | char(0x9d), 104 | char(0xb7), 105 | char(0xe2), 106 | char(0x9d), 107 | char(0xb8)}; 108 | 109 | REQUIRE(user.nickname.size() == sizeof(default_nickname)); 110 | REQUIRE(std::equal(user.nickname.begin(), user.nickname.end(), default_nickname)); 111 | 112 | REQUIRE(user.birthday == create_date(0, 0, 0)); 113 | REQUIRE(user.dark_history.empty()); 114 | REQUIRE(user.optional_attributes.empty()); 115 | REQUIRE(!user.block_event); 116 | 117 | BlockEvent event; 118 | 119 | REQUIRE(event.admin_ID == 255ULL); 120 | REQUIRE(event.date == create_date(1970, 1, 1)); 121 | REQUIRE(event.serial_number == 0ULL); 122 | REQUIRE(event.details.empty()); 123 | } 124 | 125 | TEST_CASE("Test for correct parsing (old)", "[parsing]") 126 | { 127 | SECTION("Test for an array of user", "[parsing]") 128 | { 129 | std::vector users; 130 | ParsingResult err; 131 | 132 | bool success = from_json_file( 133 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array.json", users, err); 134 | { 135 | CAPTURE(err.description()); 136 | REQUIRE(success); 137 | } 138 | REQUIRE(users.size() == 2); 139 | 140 | { 141 | const User& u = users.front(); 142 | REQUIRE(u.ID == 7947402710862746952ULL); 143 | REQUIRE(u.nickname == "bigger than bigger"); 144 | REQUIRE(u.birthday == create_date(1984, 9, 2)); 145 | 146 | REQUIRE(u.block_event.get() != nullptr); 147 | const BlockEvent& e = *u.block_event; 148 | 149 | REQUIRE(e.admin_ID > 0ULL); 150 | REQUIRE(e.date == create_date(1970, 12, 31)); 151 | REQUIRE(e.description == "advertisement"); 152 | REQUIRE(e.details.size() > 0ULL); 153 | 154 | REQUIRE(u.dark_history.empty()); 155 | REQUIRE(u.optional_attributes.empty()); 156 | } 157 | 158 | { 159 | const User& u = users.back(); 160 | REQUIRE(u.ID == 13478355757133566847ULL); 161 | REQUIRE(u.nickname.size() == 15); 162 | REQUIRE(!u.block_event); 163 | REQUIRE(u.optional_attributes.size() == 3); 164 | REQUIRE(u.optional_attributes.find("Self description") != u.optional_attributes.end()); 165 | } 166 | } 167 | 168 | SECTION("Test for a map of user", "[parsing]") 169 | { 170 | std::unordered_map users; 171 | ParsingResult err; 172 | 173 | bool success = from_json_file( 174 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_map.json", users, err); 175 | { 176 | CAPTURE(err.description()); 177 | REQUIRE(success); 178 | } 179 | REQUIRE(users.size() == 2); 180 | 181 | { 182 | const User& u = users["First"]; 183 | REQUIRE(u.ID == 7947402710862746952ULL); 184 | REQUIRE(u.nickname == "bigger than bigger"); 185 | REQUIRE(u.birthday == create_date(1984, 9, 2)); 186 | 187 | REQUIRE(u.block_event.get() != nullptr); 188 | const BlockEvent& e = *u.block_event; 189 | 190 | REQUIRE(e.admin_ID > 0ULL); 191 | REQUIRE(e.date == create_date(1970, 12, 31)); 192 | REQUIRE(e.description == "advertisement"); 193 | REQUIRE(e.details.size() > 0ULL); 194 | 195 | REQUIRE(u.dark_history.empty()); 196 | REQUIRE(u.optional_attributes.empty()); 197 | } 198 | 199 | { 200 | const User& u = users["Second"]; 201 | REQUIRE(u.ID == 13478355757133566847ULL); 202 | REQUIRE(u.nickname.size() == 15); 203 | REQUIRE(!u.block_event); 204 | REQUIRE(u.optional_attributes.size() == 3); 205 | REQUIRE(u.optional_attributes.find("Self description") != u.optional_attributes.end()); 206 | } 207 | } 208 | } 209 | 210 | TEST_CASE("Test for mismatch between JSON and C++ class std::vector (old)", 211 | "[parsing], [error]") 212 | { 213 | std::vector users; 214 | ParsingResult err; 215 | 216 | SECTION("Mismatch between array and object", "[parsing], [error], [type mismatch]") 217 | { 218 | REQUIRE(!from_json_file( 219 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/single_object.json", users, err)); 220 | CAPTURE(err.description()); 221 | REQUIRE(!err.error_stack().empty()); 222 | 223 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 224 | REQUIRE(std::distance(err.begin(), err.end()) == 1); 225 | 226 | auto&& e = static_cast(*err.begin()); 227 | 228 | // REQUIRE(e.expected_type() == "array"); 229 | 230 | REQUIRE(e.actual_type() == "object"); 231 | } 232 | 233 | SECTION("Required field not present; test the path as well", 234 | "[parsing], [error], [missing required]") 235 | { 236 | REQUIRE(!from_json_file( 237 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/missing_required.json", users, err)); 238 | CAPTURE(err.description()); 239 | REQUIRE(!err.error_stack().empty()); 240 | 241 | REQUIRE(std::distance(err.begin(), err.end()) == 5); 242 | 243 | auto it = err.begin(); 244 | REQUIRE(it->type() == error::MISSING_REQUIRED); 245 | 246 | ++it; 247 | REQUIRE(it->type() == error::OBJECT_MEMBER); 248 | REQUIRE(static_cast(*it).member_name() == "date"); 249 | 250 | ++it; 251 | REQUIRE(it->type() == error::ARRAY_ELEMENT); 252 | REQUIRE(static_cast(*it).index() == 0); 253 | 254 | ++it; 255 | REQUIRE(it->type() == error::OBJECT_MEMBER); 256 | REQUIRE(static_cast(*it).member_name() == "dark_history"); 257 | } 258 | 259 | SECTION("Unknown field in strict parsed class Date", "[parsing], [error], [unknown field]") 260 | { 261 | REQUIRE(!from_json_file( 262 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/unknown_field.json", users, err)); 263 | CAPTURE(err.description()); 264 | REQUIRE(!err.error_stack().empty()); 265 | 266 | REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); 267 | 268 | REQUIRE(static_cast(*err.begin()).field_name() == "hour"); 269 | } 270 | 271 | SECTION("Duplicate key in class User", "[parsing], [error], [duplicate key]") 272 | { 273 | REQUIRE(!from_json_file( 274 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/duplicate_key_user.json", users, err)); 275 | CAPTURE(err.description()); 276 | REQUIRE(!err.error_stack().empty()); 277 | 278 | REQUIRE(err.begin()->type() == error::DUPLICATE_KEYS); 279 | 280 | REQUIRE(static_cast(*err.begin()).key() == "ID"); 281 | } 282 | 283 | SECTION("Out of range", "[parsing], [error], [out of range]") 284 | { 285 | REQUIRE(!from_json_file( 286 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/out_of_range.json", users, err)); 287 | CAPTURE(err.description()); 288 | REQUIRE(!err.error_stack().empty()); 289 | 290 | REQUIRE(err.begin()->type() == error::NUMBER_OUT_OF_RANGE); 291 | } 292 | 293 | SECTION("Mismatch between integer and string", "[parsing], [error], [type mismatch]") 294 | { 295 | REQUIRE(!from_json_file( 296 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/integer_string.json", users, err)); 297 | CAPTURE(err.description()); 298 | REQUIRE(!err.error_stack().empty()); 299 | 300 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 301 | } 302 | 303 | SECTION("Null character in key", "[parsing], [error], [null character]") 304 | { 305 | REQUIRE(!from_json_file( 306 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/null_in_key.json", users, err)); 307 | CAPTURE(err.description()); 308 | REQUIRE(!err.error_stack().empty()); 309 | 310 | REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); 311 | } 312 | } 313 | 314 | TEST_CASE("Test for mismatch between JSON and C++ class std::map (old)", 315 | "[parsing], [error]") 316 | { 317 | std::map users; 318 | ParsingResult err; 319 | 320 | SECTION("Mismatch between object and array", "[parsing], [error], [type mismatch]") 321 | { 322 | REQUIRE(!from_json_file( 323 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array.json", users, err)); 324 | CAPTURE(err.description()); 325 | REQUIRE(!err.error_stack().empty()); 326 | 327 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 328 | 329 | auto&& e = static_cast(*err.begin()); 330 | // REQUIRE(e.expected_type() == "object"); 331 | REQUIRE(e.actual_type() == "array"); 332 | } 333 | 334 | SECTION("Mismatch in mapped element", "[parsing], [error], [type mismatch]") 335 | { 336 | REQUIRE(!from_json_file( 337 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/map_element_mismatch.json", users, err)); 338 | CAPTURE(err.description()); 339 | REQUIRE(!err.error_stack().empty()); 340 | { 341 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 342 | 343 | auto&& e = static_cast(*err.begin()); 344 | (void)e; 345 | } 346 | { 347 | auto it = ++err.begin(); 348 | REQUIRE(it != err.end()); 349 | REQUIRE(it->type() == error::OBJECT_MEMBER); 350 | auto&& e = static_cast(*it); 351 | REQUIRE(e.member_name() == "Third"); 352 | } 353 | } 354 | } 355 | 356 | TEST_CASE("Test for writing JSON (old)", "[serialization]") 357 | { 358 | std::vector users; 359 | ParsingResult err; 360 | 361 | bool success = from_json_file( 362 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array.json", users, err); 363 | { 364 | CAPTURE(err.description()); 365 | REQUIRE(success); 366 | } 367 | REQUIRE(users.size() == 2); 368 | 369 | std::string str = to_json_string(users); 370 | std::vector copied_users; 371 | 372 | success = from_json_string(str, copied_users, err); 373 | { 374 | CAPTURE(err.description()); 375 | REQUIRE(success); 376 | } 377 | REQUIRE(users == copied_users); 378 | } 379 | 380 | TEST_CASE("Test for DOM support (old)", "[DOM]") 381 | { 382 | rapidjson::Document doc; 383 | ParsingResult err; 384 | bool success = from_json_file( 385 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array_compact.json", doc, err); 386 | { 387 | CAPTURE(err.description()); 388 | REQUIRE(success); 389 | } 390 | 391 | SECTION("Test for parsed result", "[DOM], [parsing]") 392 | { 393 | REQUIRE(doc.IsArray()); 394 | REQUIRE(doc.Size() == 2); 395 | 396 | const rapidjson::Value& second = doc[1u]; 397 | REQUIRE(second["ID"].IsUint64()); 398 | REQUIRE(second["ID"].GetUint64() == 13478355757133566847ULL); 399 | REQUIRE(second["block_event"].IsNull()); 400 | REQUIRE(second["dark_history"].IsArray()); 401 | REQUIRE(second["dark_history"][0u].IsObject()); 402 | REQUIRE(second["dark_history"][0u]["description"] == "copyright infringement"); 403 | } 404 | 405 | SECTION("Test for serialization", "[DOM], [serialization]") 406 | { 407 | std::string output; 408 | to_json_string(output, doc); 409 | REQUIRE( 410 | output 411 | == read_all(AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array_compact.json")); 412 | } 413 | 414 | SECTION("Test for to/from DOM", "[DOM], [conversion]") 415 | { 416 | std::vector users; 417 | error::ErrorStack errs; 418 | 419 | REQUIRE(from_document(users, doc, errs)); 420 | 421 | REQUIRE(users.size() == 2); 422 | REQUIRE(users[0].birthday == create_date(1984, 9, 2)); 423 | REQUIRE(users[0].block_event); 424 | REQUIRE(users[0].block_event->details == "most likely a troll"); 425 | 426 | rapidjson::Document another_doc; 427 | to_document(users, another_doc); 428 | REQUIRE(doc == another_doc); 429 | } 430 | } 431 | 432 | TEST_CASE("Test for parsing tuple type (old)", "[parsing], [tuple]") 433 | { 434 | typedef std::tuple>, 439 | bool> 440 | hard_type; 441 | hard_type hetero_array; 442 | ParsingResult err; 443 | 444 | SECTION("Test for valid tuple", "[parsing], [tuple]") 445 | { 446 | bool success = from_json_file( 447 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/hard.json", hetero_array, err); 448 | { 449 | CAPTURE(err.description()); 450 | REQUIRE(success); 451 | } 452 | REQUIRE(std::get<1>(hetero_array) == -65535); 453 | REQUIRE(std::get::value - 1>(hetero_array) == false); 454 | } 455 | 456 | SECTION("Test for invalid tuple", "[parsing], [tuple], [error]") 457 | { 458 | REQUIRE(!from_json_file( 459 | AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/hard.json", hetero_array, err)); 460 | CAPTURE(err.description()); 461 | REQUIRE(!err.error_stack().empty()); 462 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 463 | REQUIRE(static_cast(*err.begin()).actual_type() == "null"); 464 | } 465 | } 466 | -------------------------------------------------------------------------------- /test/test_basic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "catch.hpp" 4 | 5 | using namespace staticjson; 6 | 7 | struct MyObject 8 | { 9 | int i; 10 | 11 | void staticjson_init(ObjectHandler* h) 12 | { 13 | h->set_flags(Flags::DisallowUnknownKey); 14 | h->add_property("i", &i); 15 | } 16 | }; 17 | 18 | TEST_CASE("Basic test") 19 | { 20 | MyObject obj; 21 | const char* input = "{\"i\": -980008}"; 22 | REQUIRE(from_json_string(input, &obj, nullptr)); 23 | REQUIRE(obj.i == -980008); 24 | } 25 | 26 | TEST_CASE("Failure test") 27 | { 28 | MyObject obj; 29 | const char* input = ("{\"i\": -980008, \"j\": 42}"); 30 | ParseStatus res; 31 | REQUIRE(!from_json_string(input, &obj, &res)); 32 | CAPTURE(res.description()); 33 | REQUIRE(obj.i == -980008); 34 | } 35 | 36 | TEST_CASE("Vector test") 37 | { 38 | std::vector integers; 39 | const char* input = ("[1,2,3,4,5,6]"); 40 | ParseStatus res; 41 | bool success = from_json_string(input, &integers, nullptr); 42 | CAPTURE(res.description()); 43 | REQUIRE(success); 44 | REQUIRE(integers.size() == 6); 45 | } 46 | 47 | TEST_CASE("Serial") 48 | { 49 | REQUIRE(to_json_string(123) == "123"); 50 | MyObject obj; 51 | obj.i = 999; 52 | REQUIRE(to_pretty_json_string(obj).size() > 0); 53 | REQUIRE(to_json_string(std::vector{1, 2, 3, 4, 5, 6}) == "[1,2,3,4,5,6]"); 54 | } -------------------------------------------------------------------------------- /test/test_example.cpp: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | 3 | #include 4 | #include 5 | 6 | int builtin_test() 7 | { 8 | using namespace staticjson; 9 | std::string a = to_json_string(std::vector{1.0, 2.0, -3.1415}); 10 | std::string b 11 | = to_pretty_json_string(std::map>>{}); 12 | 13 | std::vector> data; 14 | 15 | const char* json_string = "[{\" hello \": 535353, \" world \": 849}, {\" k \": -548343}]"; 16 | assert(from_json_string(json_string, &data, nullptr)); 17 | assert(data.size() == 2); 18 | assert(data[1][" k "] == -548343); 19 | to_pretty_json_file("/tmp/B11FA212-ED7F-4577-83B1-CC7ADBBC8ED3.json", data); 20 | return 0; 21 | } 22 | 23 | #include "catch.hpp" 24 | 25 | TEST_CASE("Example test") { REQUIRE(builtin_test() == 0); } -------------------------------------------------------------------------------- /test/test_instantiation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define INSTANTIATE(type) \ 5 | { \ 6 | staticjson::Handler h(nullptr); \ 7 | } 8 | 9 | // Ensure that the template classes can be instantiated without compile time error 10 | void instantiate_all_types() 11 | { 12 | INSTANTIATE(char) 13 | INSTANTIATE(bool) 14 | INSTANTIATE(int) 15 | INSTANTIATE(unsigned) 16 | INSTANTIATE(std::int64_t) 17 | INSTANTIATE(std::uint64_t) 18 | INSTANTIATE(std::string) 19 | typedef std::array my_array; 20 | INSTANTIATE(my_array) 21 | INSTANTIATE(std::vector) 22 | INSTANTIATE(std::vector) 23 | INSTANTIATE(std::deque>>>>); 24 | INSTANTIATE(staticjson::Document); 25 | INSTANTIATE(decltype(nullptr)); 26 | 27 | typedef std::tuple> complex_tuple_type; 28 | INSTANTIATE(complex_tuple_type); 29 | } 30 | 31 | #undef INSTANTIATE 32 | -------------------------------------------------------------------------------- /test/test_integration.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | #include "myarray.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #ifdef __has_include 18 | #if __has_include() 19 | #pragma message("INFO: available for testing") 20 | #include 21 | #define STATICJSON_OPTIONAL 1 22 | #endif 23 | #endif 24 | 25 | #ifdef _MSC_VER 26 | #define stat _stat 27 | #endif 28 | 29 | const std::string& get_base_dir() 30 | { 31 | struct Initializer 32 | { 33 | std::string path; 34 | 35 | Initializer() 36 | { 37 | path = "."; 38 | struct stat st; 39 | for (int i = 0; i < 16; ++i) 40 | { 41 | if (::stat((path + "/examples").c_str(), &st) == 0) 42 | { 43 | return; 44 | } 45 | else 46 | { 47 | path += "/.."; 48 | } 49 | } 50 | fprintf(stderr, 51 | "%s", 52 | "No 'examples' directory found in the working directory or its ancestors\n"); 53 | std::abort(); 54 | } 55 | }; 56 | 57 | static const Initializer init; 58 | return init.path; 59 | } 60 | 61 | using namespace staticjson; 62 | 63 | enum class CalendarType 64 | { 65 | Gregorian, 66 | Chinese, 67 | Jewish, 68 | Islam 69 | }; 70 | 71 | STATICJSON_DECLARE_ENUM(CalendarType, 72 | {"Gregorian", CalendarType::Gregorian}, 73 | {"Chinese", CalendarType::Chinese}, 74 | {"Jewish", CalendarType::Jewish}, 75 | {"Islam", CalendarType::Islam}) 76 | 77 | struct Date 78 | { 79 | int year, month, day; 80 | CalendarType type; 81 | 82 | Date() : year(0), month(0), day(0), type(CalendarType::Gregorian) {} 83 | }; 84 | 85 | namespace staticjson 86 | { 87 | void init(Date* d, ObjectHandler* h) 88 | { 89 | h->add_property("year", &d->year); 90 | h->add_property("month", &d->month); 91 | h->add_property("day", &d->day); 92 | h->add_property("type", &d->type, Flags::Optional); 93 | h->set_flags(Flags::DisallowUnknownKey); 94 | } 95 | } 96 | 97 | struct BlockEvent 98 | { 99 | std::uint64_t serial_number, admin_ID = 255; 100 | Date date; 101 | std::string description, details; 102 | #ifdef STATICJSON_OPTIONAL 103 | staticjson::optional flags; 104 | #endif 105 | 106 | void staticjson_init(ObjectHandler* h) 107 | { 108 | h->add_property("serial_number", &serial_number); 109 | h->add_property("administrator ID", &admin_ID, Flags::Optional); 110 | h->add_property("date", &date, Flags::Optional); 111 | h->add_property("description", &description, Flags::Optional); 112 | h->add_property("details", &details, Flags::Optional); 113 | #ifdef STATICJSON_OPTIONAL 114 | h->add_property("flags", &flags, Flags::Optional); 115 | #endif 116 | } 117 | }; 118 | 119 | struct User 120 | { 121 | friend class Handler; 122 | 123 | unsigned long long ID; 124 | std::string nickname; 125 | Date birthday; 126 | std::shared_ptr block_event; 127 | std::vector dark_history; 128 | std::unordered_map optional_attributes; 129 | 130 | std::tuple>, bool> auxiliary; 131 | 132 | #ifdef STATICJSON_OPTIONAL 133 | staticjson::optional dark_event; 134 | staticjson::optional>> alternate_history; 135 | #endif 136 | }; 137 | 138 | namespace staticjson 139 | { 140 | template <> 141 | class Handler : public ObjectHandler 142 | { 143 | public: 144 | explicit Handler(User* user) 145 | { 146 | auto h = this; 147 | h->add_property("ID", &user->ID); 148 | h->add_property("nickname", &user->nickname); 149 | h->add_property("birthday", &user->birthday, Flags::Optional); 150 | h->add_property("block_event", &user->block_event, Flags::Optional); 151 | h->add_property("optional_attributes", &user->optional_attributes, Flags::Optional); 152 | h->add_property("dark_history", &user->dark_history, Flags::Optional); 153 | h->add_property("auxiliary", &user->auxiliary, Flags::Optional); 154 | #ifdef STATICJSON_OPTIONAL 155 | h->add_property("dark_event", &user->dark_event, Flags::Optional); 156 | h->add_property("alternate_history", &user->alternate_history, Flags::Optional); 157 | #endif 158 | } 159 | 160 | std::string type_name() const override { return "User"; } 161 | }; 162 | } 163 | 164 | inline bool operator==(Date d1, Date d2) 165 | { 166 | return d1.year == d2.year && d1.month == d2.month && d1.day == d2.day; 167 | } 168 | 169 | inline bool operator!=(Date d1, Date d2) { return !(d1 == d2); } 170 | 171 | inline bool operator==(const BlockEvent& b1, const BlockEvent& b2) 172 | { 173 | return b1.admin_ID == b2.admin_ID && b1.date == b2.date && b1.description == b2.description 174 | && b1.details == b2.details; 175 | } 176 | 177 | inline bool operator!=(const BlockEvent& b1, const BlockEvent& b2) { return !(b1 == b2); } 178 | 179 | inline bool operator==(const User& u1, const User& u2) 180 | { 181 | return u1.birthday == u2.birthday && u1.ID == u2.ID && u1.nickname == u2.nickname 182 | && u1.dark_history == u2.dark_history && u1.optional_attributes == u2.optional_attributes 183 | && ((!u1.block_event && !u2.block_event) 184 | || ((u1.block_event && u2.block_event) && *u1.block_event == *u2.block_event)); 185 | } 186 | 187 | inline bool operator!=(const User& u1, const User& u2) { return !(u1 == u2); } 188 | 189 | inline std::string read_all(const std::string& file_name) 190 | { 191 | nonpublic::FileGuard fg(std::fopen(file_name.c_str(), "rb")); 192 | if (!fg.fp) 193 | throw std::system_error(errno, std::system_category()); 194 | std::string result; 195 | while (1) 196 | { 197 | int ch = getc(fg.fp); 198 | if (ch == EOF) 199 | break; 200 | result.push_back(static_cast(ch)); 201 | } 202 | return result; 203 | } 204 | 205 | inline Date create_date(int year, int month, int day) 206 | { 207 | Date d; 208 | d.year = year; 209 | d.month = month; 210 | d.day = day; 211 | return d; 212 | } 213 | 214 | void check_first_user(const User& u) 215 | { 216 | REQUIRE(u.ID == 7947402710862746952ULL); 217 | REQUIRE(u.nickname == "bigger than bigger"); 218 | REQUIRE(u.birthday == create_date(1984, 9, 2)); 219 | 220 | REQUIRE(u.block_event.get() != nullptr); 221 | const BlockEvent& e = *u.block_event; 222 | 223 | REQUIRE(e.admin_ID > 0ULL); 224 | REQUIRE(e.date == create_date(1970, 12, 31)); 225 | REQUIRE(e.description == "advertisement"); 226 | REQUIRE(e.details.size() > 0ULL); 227 | 228 | REQUIRE(u.dark_history.empty()); 229 | REQUIRE(u.optional_attributes.empty()); 230 | 231 | #ifdef STATICJSON_OPTIONAL 232 | REQUIRE(!!u.dark_event); 233 | REQUIRE(u.dark_event->serial_number == 9876543210123456789ULL); 234 | REQUIRE(u.dark_event->admin_ID == 11223344556677889900ULL); 235 | REQUIRE(u.dark_event->date == create_date(1970, 12, 31)); 236 | REQUIRE(u.dark_event->description == "promote"); 237 | REQUIRE(u.dark_event->details == "at least like a troll"); 238 | REQUIRE(static_cast(u.dark_event->flags) == false); 239 | 240 | REQUIRE(static_cast(u.alternate_history) == true); 241 | REQUIRE(u.alternate_history->size() == 2); 242 | REQUIRE(static_cast(u.alternate_history->at(0)) == false); 243 | REQUIRE(static_cast(u.alternate_history->at(1)) == true); 244 | 245 | { 246 | const auto& opt_e = u.alternate_history->at(1); 247 | REQUIRE(opt_e); 248 | const BlockEvent& e = *opt_e; 249 | REQUIRE(e.serial_number == 1123581321345589ULL); 250 | REQUIRE(e.admin_ID == 1123581321345589ULL); 251 | REQUIRE(e.date == create_date(1970, 12, 31)); 252 | REQUIRE(e.description == "redacted"); 253 | REQUIRE(e.details == "redacted"); 254 | REQUIRE(static_cast(e.flags) == true); 255 | REQUIRE(*e.flags == "x"); 256 | } 257 | #endif 258 | } 259 | 260 | void check_second_user(const User& u) 261 | { 262 | REQUIRE(u.ID == 13478355757133566847ULL); 263 | REQUIRE(u.nickname.size() == 15); 264 | REQUIRE(!u.block_event); 265 | REQUIRE(u.optional_attributes.size() == 3); 266 | REQUIRE(u.optional_attributes.find("Self description") != u.optional_attributes.end()); 267 | } 268 | 269 | template 270 | void check_array_of_user(const ArrayOfUsers& users) 271 | { 272 | REQUIRE(users.size() == 3); 273 | 274 | check_first_user(users[0]); 275 | check_second_user(users[1]); 276 | } 277 | 278 | void check_array_of_user(const Document& users) 279 | { 280 | REQUIRE(users.IsArray()); 281 | REQUIRE(users.Size() == 3); 282 | 283 | const Value& u = users[0]; 284 | REQUIRE(u.IsObject()); 285 | REQUIRE(u.HasMember("ID")); 286 | REQUIRE(u["ID"].IsUint64()); 287 | REQUIRE(u["ID"].GetUint64() == 7947402710862746952ULL); 288 | REQUIRE(u.HasMember("nickname")); 289 | REQUIRE(u["nickname"].IsString()); 290 | REQUIRE(std::strcmp(u["nickname"].GetString(), "bigger than bigger") == 0); 291 | REQUIRE(u.HasMember("birthday")); 292 | REQUIRE(u["birthday"].IsObject()); 293 | REQUIRE(u["birthday"].HasMember("year")); 294 | REQUIRE(u["birthday"]["year"] == 1984); 295 | 296 | REQUIRE(u.HasMember("block_event")); 297 | const Value& e = u["block_event"]; 298 | REQUIRE(e.HasMember("administrator ID")); 299 | REQUIRE(e.HasMember("description")); 300 | const Value& desc = e["description"]; 301 | REQUIRE(desc.IsString()); 302 | REQUIRE(std::strcmp(desc.GetString(), "advertisement") == 0); 303 | 304 | #ifdef STATICJSON_OPTIONAL 305 | REQUIRE(u.HasMember("dark_event")); 306 | { 307 | const Value& e = u["dark_event"]; 308 | REQUIRE(e.HasMember("administrator ID")); 309 | REQUIRE(e.HasMember("description")); 310 | REQUIRE(e["flags"].IsNull()); 311 | } 312 | 313 | REQUIRE(u.HasMember("alternate_history")); 314 | { 315 | const Value& e = u["alternate_history"]; 316 | REQUIRE(e.IsArray()); 317 | REQUIRE(e[0].IsNull()); 318 | REQUIRE(e[1].IsObject()); 319 | REQUIRE(e[1]["flags"].IsString()); 320 | } 321 | #endif 322 | } 323 | 324 | TEST_CASE("Test for correct parsing", "[parsing],[c]") 325 | { 326 | SECTION("Simple date parsing", "[parsing]") 327 | { 328 | Date d; 329 | ParseStatus err; 330 | bool success = from_json_string( 331 | "{\"year\": 1900, \"day\": 3, \"month\": 11, \"type\": \"Chinese\"}", &d, &err); 332 | { 333 | CAPTURE(err.description()); 334 | REQUIRE(success); 335 | } 336 | } 337 | 338 | SECTION("Test for an array of user", "[parsing]") 339 | { 340 | Array users; 341 | ParseStatus err; 342 | 343 | // Do it twice to test if vectors are reset properly at parsing. 344 | for (int i = 0; i < 2; ++i) 345 | { 346 | bool success = from_json_file( 347 | get_base_dir() + "/examples/success/user_array.json", &users, &err); 348 | { 349 | CAPTURE(err.description()); 350 | REQUIRE(success); 351 | } 352 | check_array_of_user(users); 353 | REQUIRE(users[0].birthday.type == CalendarType::Jewish); 354 | } 355 | 356 | Document d; 357 | REQUIRE(to_json_document(&d, users, nullptr)); 358 | check_array_of_user(d); 359 | } 360 | SECTION("Test for document", "[parsing]") 361 | { 362 | Document users; 363 | ParseStatus err; 364 | 365 | bool success 366 | = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); 367 | { 368 | CAPTURE(err.description()); 369 | REQUIRE(success); 370 | } 371 | check_array_of_user(users); 372 | 373 | std::vector vusers; 374 | REQUIRE(from_json_document(users, &vusers, nullptr)); 375 | check_array_of_user(vusers); 376 | } 377 | 378 | SECTION("Test for a map of user", "[parsing], [q]") 379 | { 380 | std::unordered_map users; 381 | ParseStatus err; 382 | 383 | // Do it twice to test if map is reset properly at parsing. 384 | for (int i = 0; i < 2; ++i) 385 | { 386 | bool success 387 | = from_json_file(get_base_dir() + "/examples/success/user_map.json", &users, &err); 388 | { 389 | CAPTURE(err.description()); 390 | REQUIRE(success); 391 | } 392 | REQUIRE(users.size() == 2); 393 | check_first_user(users["First"]); 394 | check_second_user(users["Second"]); 395 | } 396 | } 397 | } 398 | 399 | TEST_CASE("Test for mismatch between JSON and C++ class std::vector", 400 | "[parsing], [error]") 401 | { 402 | std::vector users; 403 | ParseStatus err; 404 | 405 | SECTION("Mismatch between array and object", "[parsing], [error], [type mismatch]") 406 | { 407 | REQUIRE( 408 | !from_json_file(get_base_dir() + "/examples/failure/single_object.json", &users, &err)); 409 | CAPTURE(err.description()); 410 | REQUIRE(!err.error_stack().empty()); 411 | 412 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 413 | REQUIRE(std::distance(err.begin(), err.end()) == 1); 414 | } 415 | 416 | SECTION("Missing requied simple", "[missing required]") 417 | { 418 | Date date; 419 | const char* str = "{ \"month\": 12 }"; 420 | REQUIRE(!from_json_string(str, &date, nullptr)); 421 | } 422 | 423 | SECTION("Required field not present; test the path as well", 424 | "[parsing], [error], [missing required]") 425 | { 426 | REQUIRE(!from_json_file( 427 | get_base_dir() + "/examples/failure/missing_required.json", &users, &err)); 428 | CAPTURE(err.description()); 429 | REQUIRE(!err.error_stack().empty()); 430 | 431 | REQUIRE(std::distance(err.begin(), err.end()) == 5); 432 | 433 | auto it = err.begin(); 434 | REQUIRE(it->type() == error::MISSING_REQUIRED); 435 | 436 | ++it; 437 | REQUIRE(it->type() == error::OBJECT_MEMBER); 438 | REQUIRE(static_cast(*it).member_name() == "date"); 439 | 440 | ++it; 441 | REQUIRE(it->type() == error::ARRAY_ELEMENT); 442 | REQUIRE(static_cast(*it).index() == 0); 443 | 444 | ++it; 445 | REQUIRE(it->type() == error::OBJECT_MEMBER); 446 | REQUIRE(static_cast(*it).member_name() == "dark_history"); 447 | } 448 | 449 | SECTION("Unknown field in strict parsed class Date", "[parsing], [error], [unknown field]") 450 | { 451 | REQUIRE( 452 | !from_json_file(get_base_dir() + "/examples/failure/unknown_field.json", &users, &err)); 453 | CAPTURE(err.description()); 454 | REQUIRE(!err.error_stack().empty()); 455 | 456 | REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); 457 | 458 | REQUIRE(static_cast(*err.begin()).field_name() == "hour"); 459 | } 460 | 461 | SECTION("Duplicate key in class User", "[parsing], [error], [duplicate key]") 462 | { 463 | REQUIRE(!from_json_file( 464 | get_base_dir() + "/examples/failure/duplicate_key_user.json", &users, &err)); 465 | CAPTURE(err.description()); 466 | REQUIRE(!err.error_stack().empty()); 467 | 468 | REQUIRE(err.begin()->type() == error::DUPLICATE_KEYS); 469 | 470 | REQUIRE(static_cast(*err.begin()).key() == "ID"); 471 | } 472 | 473 | SECTION("Out of range", "[parsing], [error], [out of range]") 474 | { 475 | REQUIRE( 476 | !from_json_file(get_base_dir() + "/examples/failure/out_of_range.json", &users, &err)); 477 | CAPTURE(err.description()); 478 | REQUIRE(!err.error_stack().empty()); 479 | 480 | REQUIRE(err.begin()->type() == error::NUMBER_OUT_OF_RANGE); 481 | } 482 | 483 | SECTION("Mismatch between integer and string", "[parsing], [error], [type mismatch]") 484 | { 485 | REQUIRE(!from_json_file( 486 | get_base_dir() + "/examples/failure/integer_string.json", &users, &err)); 487 | CAPTURE(err.description()); 488 | REQUIRE(!err.error_stack().empty()); 489 | 490 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 491 | } 492 | 493 | SECTION("Null character in key", "[parsing], [error], [null character]") 494 | { 495 | REQUIRE( 496 | !from_json_file(get_base_dir() + "/examples/failure/null_in_key.json", &users, &err)); 497 | CAPTURE(err.description()); 498 | REQUIRE(!err.error_stack().empty()); 499 | 500 | REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); 501 | } 502 | } 503 | 504 | TEST_CASE("Test for mismatch between JSON and C++ class std::map", 505 | "[parsing], [error]") 506 | { 507 | std::map users; 508 | ParseStatus err; 509 | 510 | SECTION("Mismatch between object and array", "[parsing], [error], [type mismatch]") 511 | { 512 | REQUIRE( 513 | !from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err)); 514 | CAPTURE(err.description()); 515 | REQUIRE(!err.error_stack().empty()); 516 | 517 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 518 | 519 | auto&& e = static_cast(*err.begin()); 520 | REQUIRE(e.actual_type() == "array"); 521 | } 522 | 523 | SECTION("Mismatch in mapped element", "[parsing], [error], [type mismatch]") 524 | { 525 | REQUIRE(!from_json_file( 526 | get_base_dir() + "/examples/failure/map_element_mismatch.json", &users, &err)); 527 | CAPTURE(err.description()); 528 | REQUIRE(!err.error_stack().empty()); 529 | { 530 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 531 | 532 | auto&& e = static_cast(*err.begin()); 533 | (void)e; 534 | } 535 | { 536 | auto it = ++err.begin(); 537 | REQUIRE(it != err.end()); 538 | REQUIRE(it->type() == error::OBJECT_MEMBER); 539 | auto&& e = static_cast(*it); 540 | REQUIRE(e.member_name() == "Third"); 541 | } 542 | } 543 | 544 | SECTION("Invalid enum", "[parsing], [error], [enum]") 545 | { 546 | REQUIRE( 547 | !from_json_file(get_base_dir() + "/examples/failure/invalid_enum.json", &users, &err)); 548 | CAPTURE(err.description()); 549 | REQUIRE(!err.error_stack().empty()); 550 | 551 | REQUIRE(err.begin()->type() == error::INVALID_ENUM); 552 | const auto& e = static_cast(*err.begin()); 553 | REQUIRE(e.description() == "\"West\" is an invalid enum name"); 554 | } 555 | } 556 | 557 | TEST_CASE("Test for max leaf number check", "[serialization]") 558 | { 559 | std::vector users, reparsed_users; 560 | ParseStatus err; 561 | 562 | bool success 563 | = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); 564 | { 565 | CAPTURE(err.description()); 566 | REQUIRE(success); 567 | } 568 | REQUIRE(users.size() == 3); 569 | 570 | std::string json_output = to_pretty_json_string(users); 571 | CAPTURE(json_output); 572 | staticjson::GlobalConfig::getInstance()->setMaxLeaves(20); 573 | success = from_json_string(json_output.c_str(), &reparsed_users, &err); 574 | staticjson::GlobalConfig::getInstance()->unsetMaxLeavesFlag(); 575 | { 576 | CAPTURE(err.description()); 577 | REQUIRE(!success); 578 | } 579 | 580 | REQUIRE(err.description().find("Too many leaves") != std::string::npos); 581 | } 582 | 583 | TEST_CASE("Test for max depth check", "[serialization]") 584 | { 585 | std::vector users, reparsed_users; 586 | ParseStatus err; 587 | staticjson::GlobalConfig::getInstance()->setMaxDepth(3); 588 | bool success 589 | = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); 590 | staticjson::GlobalConfig::getInstance()->unsetMaxDepthFlag(); 591 | 592 | { 593 | CAPTURE(err.description()); 594 | REQUIRE(!success); 595 | } 596 | REQUIRE(err.description().find("Too many levels of recursion") != std::string::npos); 597 | } 598 | 599 | TEST_CASE("Test for writing JSON", "[serialization]") 600 | { 601 | std::vector users, reparsed_users; 602 | ParseStatus err; 603 | 604 | bool success 605 | = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); 606 | { 607 | CAPTURE(err.description()); 608 | REQUIRE(success); 609 | } 610 | REQUIRE(users.size() == 3); 611 | 612 | std::string json_output = to_pretty_json_string(users); 613 | CAPTURE(json_output); 614 | 615 | success = from_json_string(json_output.c_str(), &reparsed_users, &err); 616 | { 617 | CAPTURE(err.description()); 618 | REQUIRE(success); 619 | } 620 | 621 | REQUIRE(users == reparsed_users); 622 | } 623 | 624 | static bool is_valid_json(const std::string& filename, rapidjson::SchemaValidator* validator) 625 | { 626 | Document d; 627 | REQUIRE(from_json_file(filename, &d, nullptr)); 628 | bool rc = d.Accept(*validator); 629 | validator->Reset(); 630 | return rc; 631 | } 632 | 633 | TEST_CASE("Schema generation and validation") 634 | { 635 | const char* invalid_json_filenames[] = {"out_of_range.json", 636 | "integer_string.json", 637 | "missing_required.json", 638 | "map_element_mismatch.json", 639 | "null_in_key.json", 640 | "single_object.json", 641 | "unknown_field.json", 642 | "invalid_enum.json"}; 643 | { 644 | std::vector users; 645 | Document schema = export_json_schema(&users); 646 | rapidjson::SchemaDocument sd(schema); 647 | rapidjson::SchemaValidator validator(sd); 648 | 649 | REQUIRE(is_valid_json(get_base_dir() + "/examples/success/user_array.json", &validator)); 650 | REQUIRE(is_valid_json(get_base_dir() + "/examples/success/user_array_compact.json", 651 | &validator)); 652 | 653 | for (const char* fn : invalid_json_filenames) 654 | REQUIRE(!is_valid_json(get_base_dir() + "/examples/failure/" + fn, &validator)); 655 | } 656 | { 657 | std::map users; 658 | Document schema = export_json_schema(&users); 659 | rapidjson::SchemaDocument sd(schema); 660 | rapidjson::SchemaValidator validator(sd); 661 | 662 | REQUIRE(is_valid_json(get_base_dir() + "/examples/success/user_map.json", &validator)); 663 | for (const char* fn : invalid_json_filenames) 664 | REQUIRE(!is_valid_json(get_base_dir() + "/examples/failure/" + fn, &validator)); 665 | } 666 | } 667 | -------------------------------------------------------------------------------- /test/test_memory_usage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "catch.hpp" 4 | 5 | using namespace staticjson; 6 | 7 | namespace 8 | { 9 | const std::string COMPLEX_VALUES = "complex_values"; 10 | 11 | struct Simple 12 | { 13 | std::vector floats; 14 | 15 | void staticjson_init(ObjectHandler* h) { h->add_property("floats", &floats); } 16 | }; 17 | 18 | struct Struct 19 | { 20 | std::string name; 21 | std::vector, std::list>> complex_values; 22 | Simple simple; 23 | 24 | void staticjson_init(ObjectHandler* h) 25 | { 26 | h->add_property("name", &name); 27 | h->add_property(COMPLEX_VALUES, &complex_values, staticjson::Flags::AllowDuplicateKey); 28 | h->add_property("simple", &simple); 29 | } 30 | }; 31 | } 32 | 33 | TEST_CASE("Test memory usage") 34 | { 35 | size_t memory_usage_before = 0, memory_usage_after = 0; 36 | std::string serialized; 37 | 38 | std::vector structs(100); 39 | for (size_t i = 0; i < structs.size(); ++i) 40 | { 41 | structs[i].name = "Struct" + std::to_string(i); 42 | structs[i].complex_values.resize(i); 43 | for (size_t j = 0; j < i; ++j) 44 | { 45 | auto&& v = structs[i].complex_values[j]; 46 | std::get<0>(v).push_back(j); 47 | for (size_t k = 0; k < 2 * j; ++k) 48 | { 49 | std::get<1>(v).emplace_back(std::to_string(k)); 50 | } 51 | } 52 | } 53 | Handler> h(&structs); 54 | serialized = nonpublic::serialize_json_string(&h); 55 | memory_usage_before = h.get_internal_handler().get_memory_pool().Capacity(); 56 | 57 | structs.clear(); 58 | h.prepare_for_reuse(); 59 | 60 | ParseStatus status; 61 | REQUIRE(nonpublic::parse_json_string(serialized.c_str(), &h, &status)); 62 | memory_usage_after = h.get_internal_handler().get_memory_pool().Capacity(); 63 | 64 | printf("Memory usage: before %zu, after %zu\n", memory_usage_before, memory_usage_after); 65 | CHECK(memory_usage_before == memory_usage_after); 66 | } 67 | -------------------------------------------------------------------------------- /test/test_tensor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "catch.hpp" 4 | #include "myarray.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace staticjson; 12 | 13 | const std::string& get_base_dir(); 14 | 15 | TEST_CASE("Success tensor") 16 | { 17 | std::list, 3>> tensor; 18 | ParseStatus err; 19 | 20 | bool success = from_json_file(get_base_dir() + "/examples/success/tensor.json", &tensor, &err); 21 | { 22 | CAPTURE(err.description()); 23 | REQUIRE(success); 24 | } 25 | 26 | REQUIRE(tensor.size() == 4); 27 | REQUIRE(!tensor.back().empty()); 28 | REQUIRE(tensor.back().back().empty()); 29 | auto&& first = tensor.front(); 30 | 31 | REQUIRE(first.size() == 3); 32 | REQUIRE(first[0].size() == 3); 33 | REQUIRE(first[0][0] == 1); 34 | } 35 | 36 | TEST_CASE("Error tensor") 37 | { 38 | std::list, 2>> tensor; 39 | { 40 | ParseStatus err; 41 | REQUIRE(!from_json_file( 42 | get_base_dir() + "/examples/failure/tensor_type_mismatch.json", &tensor, &err)); 43 | REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); 44 | } 45 | { 46 | ParseStatus err; 47 | REQUIRE(!from_json_file( 48 | get_base_dir() + "/examples/failure/tensor_length_error.json", &tensor, &err)); 49 | REQUIRE(err.begin()->type() == error::ARRAY_LENGTH_MISMATCH); 50 | } 51 | } 52 | --------------------------------------------------------------------------------