Repository: netheril96/StaticJSON Branch: master Commit: 3f7a1f12bd6e Files: 65 Total size: 227.8 KB Directory structure: gitextract_e730zr57/ ├── .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 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .cmake-format.py ================================================ # ---------------------------------- # Options affecting listfile parsing # ---------------------------------- with section("parse"): # Specify structure for custom cmake functions additional_commands = { 'foo': { 'flags': ['BAR', 'BAZ'], 'kwargs': {'DEPENDS': '*', 'HEADERS': '*', 'SOURCES': '*'}}} # Override configurations per-command where available override_spec = {} # Specify variable tags. vartags = [] # Specify property tags. proptags = [] # ----------------------------- # Options affecting formatting. # ----------------------------- with section("format"): # Disable formatting entirely, making cmake-format a no-op disable = False # How wide to allow formatted cmake files line_width = 80 # How many spaces to tab for indent tab_size = 2 # If true, lines are indented using tab characters (utf-8 0x09) instead of # space characters (utf-8 0x20). In cases where the layout would # require a fractional tab character, the behavior of the fractional # indentation is governed by use_tabchars = False # If is True, then the value of this variable indicates how # fractional indentions are handled during whitespace replacement. If set to # 'use-space', fractional indentation is left as spaces (utf-8 0x20). If set # to `round-up` fractional indentation is replaced with a single tab character # (utf-8 0x09) effectively shifting the column to the next tabstop fractional_tab_policy = 'use-space' # If an argument group contains more than this many sub-groups (parg or kwarg # groups) then force it to a vertical layout. max_subgroups_hwrap = 2 # If a positional argument group contains more than this many arguments, then # force it to a vertical layout. max_pargs_hwrap = 6 # If a cmdline positional group consumes more than this many lines without # nesting, then invalidate the layout (and nest) max_rows_cmdline = 2 # If true, separate flow control names from their parentheses with a space separate_ctrl_name_with_space = False # If true, separate function names from parentheses with a space separate_fn_name_with_space = False # If a statement is wrapped to more than one line, than dangle the closing # parenthesis on its own line. dangle_parens = False # If the trailing parenthesis must be 'dangled' on its on line, then align it # to this reference: `prefix`: the start of the statement, `prefix-indent`: # the start of the statement, plus one indentation level, `child`: align to # the column of the arguments dangle_align = 'prefix' # If the statement spelling length (including space and parenthesis) is # smaller than this amount, then force reject nested layouts. min_prefix_chars = 4 # If the statement spelling length (including space and parenthesis) is larger # than the tab width by more than this amount, then force reject un-nested # layouts. max_prefix_chars = 10 # If a candidate layout is wrapped horizontally but it exceeds this many # lines, then reject the layout. max_lines_hwrap = 2 # What style line endings to use in the output. line_ending = 'unix' # Format command names consistently as 'lower' or 'upper' case command_case = 'canonical' # Format keywords consistently as 'lower' or 'upper' case keyword_case = 'unchanged' # A list of command names which should always be wrapped always_wrap = [] # If true, the argument lists which are known to be sortable will be sorted # lexicographicall enable_sort = True # If true, the parsers may infer whether or not an argument list is sortable # (without annotation). autosort = False # By default, if cmake-format cannot successfully fit everything into the # desired linewidth it will apply the last, most agressive attempt that it # made. If this flag is True, however, cmake-format will print error, exit # with non-zero status code, and write-out nothing require_valid_layout = False # A dictionary mapping layout nodes to a list of wrap decisions. See the # documentation for more information. layout_passes = {} # ------------------------------------------------ # Options affecting comment reflow and formatting. # ------------------------------------------------ with section("markup"): # What character to use for bulleted lists bullet_char = '*' # What character to use as punctuation after numerals in an enumerated list enum_char = '.' # If comment markup is enabled, don't reflow the first comment block in each # listfile. Use this to preserve formatting of your copyright/license # statements. first_comment_is_literal = False # If comment markup is enabled, don't reflow any comment block which matches # this (regex) pattern. Default is `None` (disabled). literal_comment_pattern = None # Regular expression to match preformat fences in comments default= # ``r'^\s*([`~]{3}[`~]*)(.*)$'`` fence_pattern = '^\\s*([`~]{3}[`~]*)(.*)$' # Regular expression to match rulers in comments default= # ``r'^\s*[^\w\s]{3}.*[^\w\s]{3}$'`` ruler_pattern = '^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$' # If a comment line matches starts with this pattern then it is explicitly a # trailing comment for the preceeding argument. Default is '#<' explicit_trailing_pattern = '#<' # If a comment line starts with at least this many consecutive hash # characters, then don't lstrip() them off. This allows for lazy hash rulers # where the first hash char is not separated by space hashruler_min_length = 10 # If true, then insert a space between the first hash char and remaining hash # chars in a hash ruler, and normalize its length to fill the column canonicalize_hashrulers = True # enable comment markup parsing and reflow enable_markup = True # ---------------------------- # Options affecting the linter # ---------------------------- with section("lint"): # a list of lint codes to disable disabled_codes = [] # regular expression pattern describing valid function names function_pattern = '[0-9a-z_]+' # regular expression pattern describing valid macro names macro_pattern = '[0-9A-Z_]+' # regular expression pattern describing valid names for variables with global # (cache) scope global_var_pattern = '[A-Z][0-9A-Z_]+' # regular expression pattern describing valid names for variables with global # scope (but internal semantic) internal_var_pattern = '_[A-Z][0-9A-Z_]+' # regular expression pattern describing valid names for variables with local # scope local_var_pattern = '[a-z][a-z0-9_]+' # regular expression pattern describing valid names for privatedirectory # variables private_var_pattern = '_[0-9a-z_]+' # regular expression pattern describing valid names for public directory # variables public_var_pattern = '[A-Z][0-9A-Z_]+' # regular expression pattern describing valid names for function/macro # arguments and loop variables. argument_var_pattern = '[a-z][a-z0-9_]+' # regular expression pattern describing valid names for keywords used in # functions or macros keyword_pattern = '[A-Z][0-9A-Z_]+' # In the heuristic for C0201, how many conditionals to match within a loop in # before considering the loop a parser. max_conditionals_custom_parser = 2 # Require at least this many newlines between statements min_statement_spacing = 1 # Require no more than this many newlines between statements max_statement_spacing = 2 max_returns = 6 max_branches = 12 max_arguments = 5 max_localvars = 15 max_statements = 50 # ------------------------------- # Options affecting file encoding # ------------------------------- with section("encode"): # If true, emit the unicode byte-order mark (BOM) at the start of the file emit_byteorder_mark = False # Specify the encoding of the input file. Defaults to utf-8 input_encoding = 'utf-8' # Specify the encoding of the output file. Defaults to utf-8. Note that cmake # only claims to support utf-8 so be careful when using anything else output_encoding = 'utf-8' # ------------------------------------- # Miscellaneous configurations options. # ------------------------------------- with section("misc"): # A dictionary containing any per-command configuration overrides. Currently # only `command_case` is supported. per_command = {} ================================================ FILE: .github/workflows/ccpp.yaml ================================================ name: C/C++ CI on: [push, pull_request] env: VERBOSE: 1 jobs: build_matrix: strategy: matrix: config: [ [windows-latest, x64-windows, OFF], [macos-latest, x64-osx, ON], [ubuntu-20.04, x64-linux, ON], ] runs-on: ${{ matrix.config[0] }} steps: - uses: actions/checkout@v1 - name: vcpkg build uses: johnwason/vcpkg-action@v4 with: pkgs: rapidjson catch2 triplet: ${{ matrix.config[1] }} token: ${{ github.token }} - name: configure 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] }} .. - name: build run: cmake --build build --config Debug - name: test run: cd build && ctest -V -C Debug ================================================ FILE: .gitignore ================================================ .DS_Store ._* xcode/ *.backup # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.appCMakeCache.txt CMakeFiles CMakeScripts Makefile cmake_install.cmake install_manifest.txt CTestTestfile.cmake rapidjson.tgz rapidjson*/ build/ .vscode/ ================================================ FILE: .travis.yml ================================================ language: cpp sudo: required dist: trusty script: "sudo apt-get install -y cmake && cmake . && make && ctest -V" ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.0) project(StaticJSON) option(STATICJSON_ENABLE_TEST "Enable building test for StaticJSON" ON) option(STATICJSON_ASAN "Enable address sanitizer on non-MSVC" OFF) set(CMAKE_CXX_STANDARD_REQUIRED 0) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() if(MSVC) add_compile_definitions(_CRT_SECURE_NO_WARNINGS=1) else() if(STATICJSON_ASAN) add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) endif() endif() find_path(RAPIDJSON_INCLUDE_DIR rapidjson/rapidjson.h) set(SOURCE_FILES src/staticjson.cpp) add_library(staticjson ${SOURCE_FILES}) set_property(TARGET staticjson PROPERTY CXX_STANDARD 11) add_library(staticjson::staticjson ALIAS staticjson) target_include_directories( staticjson PUBLIC $ $ $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) if(STATICJSON_ENABLE_TEST) set(TARGET test_staticjson) file(GLOB SOURCES test/*.hpp test/*.cpp include/staticjson/*.hpp) add_executable(${TARGET} ${SOURCES}) target_link_libraries(${TARGET} PRIVATE staticjson) set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 17) find_package(RapidJSON CONFIG REQUIRED) target_link_libraries(${TARGET} PRIVATE rapidjson) find_package(Catch2 CONFIG REQUIRED) target_link_libraries(${TARGET} PRIVATE Catch2::Catch2 Catch2::Catch2WithMain) enable_testing() add_test( NAME ${TARGET} COMMAND ${TARGET} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) endif() include(GNUInstallDirs) install( TARGETS staticjson EXPORT staticjson-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/staticjson DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install( EXPORT staticjson-targets FILE staticjson-targets.cmake NAMESPACE staticjson:: DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/staticjson) include(CMakePackageConfigHelpers) configure_package_config_file( ${CMAKE_SOURCE_DIR}/cmake/staticjson-config.cmake.in ${CMAKE_BINARY_DIR}/cmake/staticjson-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/staticjson) install(FILES ${CMAKE_BINARY_DIR}/cmake/staticjson-config.cmake DESTINATION ${CMAKE_INSTALL_DATADIR}/cmake/staticjson) ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Siyuan Ren (netheril96@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # StaticJSON Fast, direct and static typed parsing of JSON with C++. ## Overview 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. 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. `StaticJSON` is an attempt to solve this problem by automating such process. ## Usage `StaticJSON` requires a C++11 compiler. ## Method 1: use [vcpkg](https://vcpkg.io) Run command `vcpkg install staticjson`, then in your `CMakeLists.txt`, add ```cmake find_package(staticjson CONFIG REQUIRED) target_link_libraries(${YOUR_TARGET} PRIVATE staticjson::staticjson) ``` ## Method 2: just build everything together 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). ## Quick start ### Builtin types ```c++ #include int builtin_test() { using namespace staticjson; std::string a = to_json_string(std::vector{1.0, 2.0, -3.1415}); std::string b = to_pretty_json_string(std::map>>{}); std::vector> data; const char* json_string = "[{\" hello \": 535353, \" world \": 849}," " {\" k \": -548343}]"; assert(from_json_string(json_string, &data, nullptr)); assert(data.size() == 2); assert(data[1][" k "] == -548343); to_pretty_json_file(stdout, data); return 0; } ``` ### Register custom class types 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. #### Intrusive definition This way requires you to implement a special method in the class prototyped `void staticjson_init(staticjson::ObjectHandler* h)`. Example definition ```c++ struct Date { int year, month, day; void staticjson_init(ObjectHandler* h) { h->add_property("year", &year); h->add_property("month", &month); h->add_property("day", &day); h->set_flags(Flags::DisallowUnknownKey); } }; struct BlockEvent { std::uint64_t serial_number, admin_ID = 255; Date date; std::string description, details; void staticjson_init(ObjectHandler* h) { h->add_property("serial_number", &serial_number); h->add_property("administrator ID", &admin_ID, Flags::Optional); h->add_property("date", &date, Flags::Optional); h->add_property("description", &description, Flags::Optional); h->add_property("details", &details, Flags::Optional); } }; ``` ### Non-intrusive definition This requires you to overload a special function for your custom class. For example, the `Date` overload is written as ```c++ namespace staticjson { void init(Date* d, ObjectHandler* h) { h->add_property("year", &d->year); h->add_property("month", &d->month); h->add_property("day", &d->day); h->set_flags(Flags::DisallowUnknownKey); } } ``` You may need to declare `staticjson::init` as a friend function in order to access private and protected members. ### Register enumeration types Example ```c++ enum class CalendarType { Gregorian, Chinese, Jewish, Islam }; STATICJSON_DECLARE_ENUM(CalendarType, {"Gregorian", CalendarType::Gregorian}, {"Chinese", CalendarType::Chinese}, {"Jewish", CalendarType::Jewish}, {"Islam", CalendarType::Islam}) ``` This will convert the enum type to/from strings, and signal error if the string is not in the list. Note that this macro must not be instantiated inside a namespace. ## Custom conversion 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` ```c++ namespace staticjson { template <> struct Converter { typedef std::string shadow_type; // This typedef is a must. The shadow type is a C++ type // that can be directly converted to and from JSON values. static std::unique_ptr from_shadow(const shadow_type& shadow, Date& value) { bool success = value.parseISO8601(shadow); if (success) return nullptr; return std::make_unique("Invalid ISO 8601 string"); } static void to_shadow(const Date& value, shadow_type& shadow) { shadow = value.toISO8601(); } }; } ``` ## Error handling `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). 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 ``` Parsing failed at offset 1000 with error code 16: Terminate parsing due to Handler error. Traceback (last call first) * Type mismatch between expected type "unsigned long long" and actual type "string" * Error at object member with name "serial_number" * Error at array element at index 0 * Error at object member with name "dark_history" * Error at array element at index 1 ``` ## List of builtin supported types * **Boolean types**: `bool`, `char` * **Integer types**: `int`, `unsigned int`, `long`, `unsigned long`, `long long`, `unsigned long long` * **Floating point types**: `float`, `double` * **String types**: `std::string` * **Array types**: `std::vector<•>`, `std::deque<•>`, `std::list<•>`, `std::array<•>` * **Nullable types**: `std::nullptr_t`, `std::unique_ptr<•>`, `std::shared_ptr<•>` * **Map types**: `std::{map, multimap, unordered_map, unordered_multimap}` * **Tuple types**: `std::tuple<...>` ## Dynamic typing 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`. * 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`. * 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`. ## Export as JSON Schema 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++. ## Misc The project was originally named *autojsoncxx* and requires a code generator to run. ================================================ FILE: _clang-format ================================================ --- Language: Cpp # BasedOnStyle: WebKit AccessModifierOffset: -4 AlignAfterOpenBracket: true AlignEscapedNewlinesLeft: false AlignOperands: false AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakTemplateDeclarations: true AlwaysBreakBeforeMultilineStrings: false BreakBeforeBinaryOperators: All BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: true BinPackParameters: false BinPackArguments: false ColumnLimit: 100 ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 DerivePointerAlignment: false ExperimentalAutoDetectBinPacking: false IndentCaseLabels: false IndentWrappedFunctionNames: false IndentFunctionDeclarationAfterType: false MaxEmptyLinesToKeep: 1 KeepEmptyLinesAtTheStartOfBlocks: true NamespaceIndentation: Inner ObjCBlockIndentWidth: 4 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakString: 1000 PenaltyBreakFirstLessLess: 120 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left SpacesBeforeTrailingComments: 4 Cpp11BracedListStyle: true Standard: Cpp11 IndentWidth: 4 TabWidth: 8 UseTab: Never BreakBeforeBraces: Allman SpacesInParentheses: false SpacesInSquareBrackets: false SpacesInAngles: false SpaceInEmptyParentheses: false SpacesInCStyleCastParentheses: false SpaceAfterCStyleCast: false SpacesInContainerLiterals: true SpaceBeforeAssignmentOperators: true ContinuationIndentWidth: 4 CommentPragmas: '^ IWYU pragma:' ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] SpaceBeforeParens: ControlStatements DisableFormat: false FixNamespaceComments: false ... ================================================ FILE: autojsoncxx/autojsoncxx.hpp ================================================ #pragma once #include #include #include namespace autojsoncxx { using ParsingResult = staticjson::ParseStatus; using staticjson::to_json_file; using staticjson::to_json_string; using staticjson::to_pretty_json_file; using staticjson::to_pretty_json_string; namespace error = staticjson::error; template inline void to_json_string(std::string& output, const ValueType& value) { output = to_json_string(value); } template inline void to_pretty_json_string(std::string& output, const ValueType& value) { output = to_pretty_json_string(value); } template inline bool from_json_string(const char* str, ValueType& value, ParsingResult& err) { return staticjson::from_json_string(str, &value, &err); } template inline bool from_json_string(const std::string& str, ValueType& value, ParsingResult& err) { return staticjson::from_json_string(str.c_str(), &value, &err); } template inline bool from_json_file(const char* str, ValueType& value, ParsingResult& err) { return staticjson::from_json_file(str, &value, &err); } template inline bool from_json_file(const std::string& str, ValueType& value, ParsingResult& err) { return staticjson::from_json_file(str, &value, &err); } template inline bool from_json_file(std::FILE* fp, ValueType& value, ParsingResult& err) { return staticjson::from_json_file(fp, &value, &err); } template void to_document(const T& value, rapidjson::Document& doc) { staticjson::to_json_document(&doc, value, nullptr); } template bool from_document(T& value, const rapidjson::Document& doc, error::ErrorStack& errs) { staticjson::ParseStatus status; bool rc = staticjson::from_json_document(doc, &value, &status); if (status.has_error()) errs.swap(status.error_stack()); return rc; } } ================================================ FILE: autojsoncxx/autojsoncxx.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # The MIT License (MIT) # # Copyright (c) 2014 Siyuan Ren (netheril96@gmail.com) # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. """ This script allows easier transition from StaticJSON's precursor `autojsoncxx`. Generates compatible C++ header files using StaticJSON's library. """ from __future__ import unicode_literals from __future__ import print_function import re import argparse import os import string import sys import io from numbers import Number # Python 2/3 compatibility layer is_python2 = sys.version_info.major == 2 if is_python2: str = unicode # simplejson has the same interface as the standard json module, but with better error messages try: import simplejson as json except ImportError: import json # base class for all custom exceptions in this unit class InvalidDefinitionError(Exception): pass class InvalidIdentifier(InvalidDefinitionError): def __init__(self, identifier): self.identifier = identifier def __str__(self): return "Invalid string for C++ identifier: " + repr(self.identifier) class InvalidNamespace(InvalidDefinitionError): def __init__(self, namespace): self.namespace = namespace def __str__(self): return "Invalid namespace: " + repr(self.namespace) class UnrecognizedOption(InvalidDefinitionError): def __init__(self, option): self.option = option def __str__(self): return "Unrecognized option: " + repr(self.option) class UnsupportedTypeError(InvalidDefinitionError): def __init__(self, type_name): self.type_name = type_name def __str__(self): return "Unsupported C++ type: " + repr(self.type_name) NOESCAPE_CHARACTERS = bytes(string.digits + string.ascii_letters + ' ', encoding='utf8') def cstring_literal(byte_string): """convert arbitrary byte sequence into a C++ string literal by escaping every character""" if all(c in NOESCAPE_CHARACTERS for c in byte_string): return '"' + byte_string.decode() + '"' return '"' + ''.join('\\x{:02x}'.format(ord(char)) for char in byte_string) + '"' def check_identifier(identifier): if not re.match(r'^[A-Za-z_]\w*$', identifier): raise InvalidIdentifier(identifier) class ClassInfo(object): accept_options = {"name", "namespace", "parse_mode", "members", "constructor_code", "comment", "no_duplicates"} def __init__(self, record): self._name = record['name'] self._members = [MemberInfo(r) for r in record['members']] self._strict = record.get('parse_mode', '') == 'strict' self._namespace = record.get("namespace", None) self._constructor_code = record.get("constructor_code", "") self._no_duplicates = record.get("no_duplicates", False) check_identifier(self._name) if self._namespace is not None and not re.match(r'^(?:::)?[A-Za-z_]\w*(?:::[A-Za-z_]\w*)*$', self._namespace): raise InvalidNamespace(self._namespace) for op in record: if op not in ClassInfo.accept_options: raise UnrecognizedOption(op) @property def name(self): return self._name @property def qualified_name(self): if self.namespace is None: return '::' + self.name if self.namespace.startswith('::'): return self.namespace + '::' + self.name return '::' + self.namespace + '::' + self.name @property def members(self): return self._members @property def strict_parsing(self): return self._strict @property def namespace(self): return self._namespace @property def constructor_code(self): return self._constructor_code @property def no_duplicates(self): return self._no_duplicates class ClassDefinitionCodeGenerator(object): def __init__(self, class_info): self._class_info = class_info @property def class_info(self): return self._class_info def member_declarations(self): return '\n'.join(m.type_name + ' ' + m.variable_name + ';' for m in self.class_info.members) def initializer_list(self): return ', '.join('{0}({1})'.format(m.variable_name, m.constructor_args) for m in self.class_info.members) def constructor(self): return 'explicit {name}():{init} {{ {code} }}\n'.format(name=self.class_info.name, init=self.initializer_list(), code=self.class_info.constructor_code) def staticjson_init(self): class_flags = 'staticjson::Flags::Default' if not self.class_info.no_duplicates: class_flags += ' | staticjson::Flags::AllowDuplicateKey' if self.class_info.strict_parsing: class_flags += ' | staticjson::Flags::DisallowUnknownKey' return """ void staticjson_init(staticjson::ObjectHandler* h) {{ {member_flag_settings} h->set_flags({class_flags}); }} """.format( class_flags=class_flags, member_flag_settings=''.join(m.add_property_statement('h') for m in self.class_info.members) ) def class_definition(self): class_def = 'struct {name} {{\n {declarations}\n\n{constructor}\n{staticjson_init}\n \n}};' \ .format(name=self.class_info.name, declarations=self.member_declarations(), constructor=self.constructor(), staticjson_init=self.staticjson_init()) if self.class_info.namespace is not None: for space in reversed(self.class_info.namespace.split('::')): if space: class_def = 'namespace {} {{ {} }}\n'.format(space, class_def) return class_def class MemberInfo(object): accept_options = {'default', 'required', 'json_key', 'comment'} def __init__(self, record): self._record = record if '*' in self.type_name or '&' in self.type_name: raise UnsupportedTypeError(self.type_name) check_identifier(self.variable_name) if len(record) > 3: raise UnrecognizedOption(record[3:]) if len(record) == 3: for op in record[2]: if op not in MemberInfo.accept_options: raise UnrecognizedOption(op) @property def type_name(self): return self._record[0] @property def variable_name(self): return self._record[1] @property def json_key(self): try: return self._record[2]['json_key'].encode('utf-8') except (IndexError, KeyError): return self.variable_name.encode('utf-8') @property def is_required(self): try: return self._record[2]['required'] except (IndexError, KeyError): return False @property def default(self): try: return self._record[2]['default'] except (IndexError, KeyError): return None @property def constructor_args(self): return MemberInfo.cpp_repr(self.default) def add_property_statement(self, handler_name): return '{}->add_property({}, &this->{}, {});\n'.format( handler_name, cstring_literal(self.json_key), self.variable_name, 'staticjson::Flags::Default' if self.is_required else 'staticjson::Flags::Optional') @staticmethod def cpp_repr(args): if args is None: return '' elif args is True: return 'true' elif args is False: return 'false' elif isinstance(args, str): return cstring_literal(args.encode('utf-8')) elif isinstance(args, Number): return str(args) elif isinstance(args, bytes): return cstring_literal(args) else: raise UnrecognizedOption("default=" + repr(args)) def read_utf8(filename): with io.open(filename, 'rt', encoding='utf-8') as f: text = f.read() if text.startswith(u'\ufeff'): # Skip BOM text = text[1:] return text def main(): parser = argparse.ArgumentParser(description='`autojsoncxx` code generator (compatibility mode)\n' '(visit https://github.com/netheril96/StaticJSON for details)') parser.add_argument('-c', '--check', help='Compatibility flag; does nothing', action='store_true', default=False) parser.add_argument('-i', '--input', help='input name for the definition file for classes', required=True) parser.add_argument('-o', '--output', help='output name for the header file', default=None) parser.add_argument('--template', help='Compatibility flag; does nothing', default=None) args = parser.parse_args() if args.output is None: args.output = os.path.basename(args.input) args.output = os.path.splitext(args.output)[0] + '.hpp' raw_record = json.loads(read_utf8(args.input)) with io.open(args.output, 'w', encoding='utf-8') as output: output.write('#pragma once\n\n') def output_class(class_record): output.write(ClassDefinitionCodeGenerator(ClassInfo(class_record)).class_definition()) output.write('\n\n') if isinstance(raw_record, list): for r in raw_record: output_class(r) else: output_class(raw_record) if __name__ == '__main__': main() ================================================ FILE: autojsoncxx/examples/failure/duplicate_key.json ================================================ [ { "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" }, "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", "note": "the duplicate key error only occurs if your map types does not support duplicate key, e.g. `std::map`" } } ] ================================================ FILE: autojsoncxx/examples/failure/duplicate_key_user.json ================================================ [ { "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" }, "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": 13478355757133566847, "nickname": "→☛∅∆ℵ", "birthday": { "year": 2001, "month": 1, "day": 23 }, "block_event": null, "ID": 3399510046441, "dark_history": [ { "serial_number": 11932018656737597978, "administrator ID": 8027685536805674999, "date": { "year": 2013, "month": 3, "day": 8 }, "description": "copyright infringement", "details": "" } ], "optional_attributes": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: autojsoncxx/examples/failure/hard.json ================================================ [{"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] ================================================ FILE: autojsoncxx/examples/failure/integer_string.json ================================================ [ { "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" }, "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": 13478355757133566847, "nickname": "→☛∅∆ℵ", "birthday": { "year": 2001, "month": 1, "day": 23 }, "block_event": null, "dark_history": [ { "serial_number": "0xa5970bbff02d9a1a", "administrator ID": 8027685536805674999, "date": { "year": 2013, "month": 3, "day": 8 }, "description": "copyright infringement", "details": "" } ], "optional_attributes": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: autojsoncxx/examples/failure/map_element_mismatch.json ================================================ { "First": { "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" }, "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" }, "Second": { "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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } }, "Third": false } ================================================ FILE: autojsoncxx/examples/failure/missing_required.json ================================================ [ { "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" } }, { "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 }, "description": "copyright infringement", "details": "" } ], "optional_attributes": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: autojsoncxx/examples/failure/null_in_key.json ================================================ [ { "ID": 7947402710862746952, "nickname": "bigger than bigger", "birthday": { "year": 1984, "month": 9, "day": 2 }, "block_event": { "serial_number": 9097588792683265916, "administrator ID": 10720293540521355122, "date": { "year\u0000 I am invisible because I am behind a null": 1970, "month": 12, "day": 31 }, "description": "advertisement", "details": "most likely a troll" }, "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: autojsoncxx/examples/failure/out_of_range.json ================================================ [ { "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": 162751169614016163 }, "description": "advertisement", "details": "most likely a troll" }, "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: autojsoncxx/examples/failure/single_object.json ================================================ { "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" } } ================================================ FILE: autojsoncxx/examples/failure/unknown_field.json ================================================ [ { "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, "hour": 21, "minute": 33, "second": 2 }, "description": "advertisement", "details": "most likely a troll" } }, { "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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: autojsoncxx/examples/success/hard.json ================================================ [{"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] ================================================ FILE: autojsoncxx/examples/success/user_array.json ================================================ [ { "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" }, "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: autojsoncxx/examples/success/user_array_compact.json ================================================ [{"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?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠"}}] ================================================ FILE: autojsoncxx/examples/success/user_map.json ================================================ { "First": { "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" }, "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" }, "Second": { "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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } } ================================================ FILE: autojsoncxx/userdef.hpp ================================================ #pragma once namespace config { struct Date { int year; int month; int day; explicit Date() : year(), month(), day() {} void staticjson_init(staticjson::ObjectHandler* h) { h->add_property("year", &this->year, staticjson::Flags::Default); h->add_property("month", &this->month, staticjson::Flags::Default); h->add_property("day", &this->day, staticjson::Flags::Default); h->set_flags(staticjson::Flags::Default | staticjson::Flags::AllowDuplicateKey | staticjson::Flags::DisallowUnknownKey); } }; } namespace config { namespace event { struct BlockEvent { unsigned long long serial_number; unsigned long long admin_ID; Date date; std::string description; std::string details; explicit BlockEvent() : serial_number() , admin_ID(255) , date() , description("\x2f\x2a\x20\x69\x6e\x69\x74\x20\x2a\x2f\x20\x74\x72\x79\x69\x6e\x67\x20" "\x74\x6f\x20\x6d\x65\x73\x73\x20\x75\x70\x20\x77\x69\x74\x68\x20\x74\x68" "\x65\x20\x63\x6f\x64\x65\x20\x67\x65\x6e\x65\x72\x61\x74\x6f\x72") , details() { date.year = 1970; date.month = 1; date.day = 1; /* Assign date to the UNIX epoch */ } void staticjson_init(staticjson::ObjectHandler* h) { h->add_property("\x73\x65\x72\x69\x61\x6c\x5f\x6e\x75\x6d\x62\x65\x72", &this->serial_number, staticjson::Flags::Default); h->add_property("administrator ID", &this->admin_ID, staticjson::Flags::Optional); h->add_property("date", &this->date, staticjson::Flags::Optional); h->add_property("description", &this->description, staticjson::Flags::Optional); h->add_property("details", &this->details, staticjson::Flags::Optional); h->set_flags(staticjson::Flags::Default | staticjson::Flags::AllowDuplicateKey); } }; } } namespace config { struct User { unsigned long long ID; std::string nickname; Date birthday; std::shared_ptr block_event; std::vector dark_history; std::map optional_attributes; explicit User() : ID() , nickname("\xe2\x9d\xb6\xe2\x9d\xb7\xe2\x9d\xb8") , birthday() , block_event() , dark_history() , optional_attributes() { } void staticjson_init(staticjson::ObjectHandler* h) { h->add_property("ID", &this->ID, staticjson::Flags::Default); h->add_property("nickname", &this->nickname, staticjson::Flags::Default); h->add_property("birthday", &this->birthday, staticjson::Flags::Optional); h->add_property("\x62\x6c\x6f\x63\x6b\x5f\x65\x76\x65\x6e\x74", &this->block_event, staticjson::Flags::Optional); h->add_property("\x64\x61\x72\x6b\x5f\x68\x69\x73\x74\x6f\x72\x79", &this->dark_history, staticjson::Flags::Optional); h->add_property( "\x6f\x70\x74\x69\x6f\x6e\x61\x6c\x5f\x61\x74\x74\x72\x69\x62\x75\x74\x65\x73", &this->optional_attributes, staticjson::Flags::Optional); h->set_flags(staticjson::Flags::Default); } }; } ================================================ FILE: autojsoncxx/userdef.json ================================================ [ { "name": "Date", "namespace": "config", "parse_mode": "strict", "members": [ ["int", "year", {"required": true}], ["int", "month", {"required": true}], ["int", "day", {"required": true}] ] }, { "name": "BlockEvent", "namespace": "config::event", "members": [ ["unsigned long long", "serial_number", {"required": true}], ["unsigned long long", "admin_ID", {"json_key": "administrator ID", "default": 255}], ["Date", "date"], ["std::string", "description", {"default": "/* init */ trying to mess up with the code generator"}], ["std::string", "details"] ], "constructor_code": "date.year = 1970; date.month = 1; date.day = 1; /* Assign date to the UNIX epoch */" }, { "name": "User", "namespace": "config", "no_duplicates": true, "members": [ ["unsigned long long", "ID", {"required": true}], ["std::string", "nickname", {"required": true, "default": "❶❷❸"} ], ["Date", "birthday"], ["std::shared_ptr", "block_event"], ["std::vector", "dark_history"], ["std::map", "optional_attributes"] ] } ] ================================================ FILE: cmake/staticjson-config.cmake.in ================================================ @PACKAGE_INIT@ include(CMakeFindDependencyMacro) find_dependency(rapidjson CONFIG) if(NOT TARGET staticjson::staticjson) include(${CMAKE_CURRENT_LIST_DIR}/staticjson-targets.cmake) endif() ================================================ FILE: examples/failure/duplicate_key.json ================================================ [ { "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" }, "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C", "note": "the duplicate key error only occurs if your map types does not support duplicate key, e.g. `std::map`" } } ] ================================================ FILE: examples/failure/duplicate_key_user.json ================================================ [ { "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" }, "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": 13478355757133566847, "nickname": "→☛∅∆ℵ", "birthday": { "year": 2001, "month": 1, "day": 23 }, "block_event": null, "ID": 3399510046441, "dark_history": [ { "serial_number": 11932018656737597978, "administrator ID": 8027685536805674999, "date": { "year": 2013, "month": 3, "day": 8 }, "description": "copyright infringement", "details": "" } ], "optional_attributes": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: examples/failure/hard.json ================================================ [{"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] ================================================ FILE: examples/failure/integer_string.json ================================================ [ { "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" }, "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": 13478355757133566847, "nickname": "→☛∅∆ℵ", "birthday": { "year": 2001, "month": 1, "day": 23 }, "block_event": null, "dark_history": [ { "serial_number": "0xa5970bbff02d9a1a", "administrator ID": 8027685536805674999, "date": { "year": 2013, "month": 3, "day": 8 }, "description": "copyright infringement", "details": "" } ], "optional_attributes": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: examples/failure/invalid_enum.json ================================================ { "First": { "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_event": { "serial_number": 9876543210123456789, "administrator ID": 11223344556677889900, "date": { "year": 1970, "month": 12, "day": 31 }, "description": "promote", "details": "at least like a troll", "flags": null }, "alternate_history": [ null, { "serial_number": 1123581321345589, "administrator ID": 1123581321345589, "date": { "year": 1970, "month": 12, "day": 31 }, "description": "redacted", "details": "redacted", "flags": "x" } ], "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" }, "Second": { "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, "type": "West" }, "description": "copyright infringement", "details": "" } ], "optional_attributes": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } } ================================================ FILE: examples/failure/map_element_mismatch.json ================================================ { "First": { "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" }, "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" }, "Second": { "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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } }, "Third": false } ================================================ FILE: examples/failure/missing_required.json ================================================ [ { "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" } }, { "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 }, "description": "copyright infringement", "details": "" } ], "optional_attributes": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: examples/failure/null_in_key.json ================================================ [ { "ID": 7947402710862746952, "nickname": "bigger than bigger", "birthday": { "year": 1984, "month": 9, "day": 2 }, "block_event": { "serial_number": 9097588792683265916, "administrator ID": 10720293540521355122, "date": { "year\u0000 I am invisible because I am behind a null": 1970, "month": 12, "day": 31 }, "description": "advertisement", "details": "most likely a troll" }, "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: examples/failure/out_of_range.json ================================================ [ { "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": 162751169614016163 }, "description": "advertisement", "details": "most likely a troll" }, "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: examples/failure/single_object.json ================================================ { "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" } } ================================================ FILE: examples/failure/tensor_length_error.json ================================================ [ [] ] ================================================ FILE: examples/failure/tensor_type_mismatch.json ================================================ [ ["1", 2, 3] ] ================================================ FILE: examples/failure/unknown_field.json ================================================ [ { "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, "hour": 21, "minute": 33, "second": 2 }, "description": "advertisement", "details": "most likely a troll" } }, { "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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } ] ================================================ FILE: examples/success/hard.json ================================================ [{"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] ================================================ FILE: examples/success/tensor.json ================================================ [ [ [1, 2, 3], [4, 5, 6], [7, 8, 0] ], [ [1.0, 2.0], [3.0, 4.0], [5.0, 6.0] ], [ [4.44], [0.9], [8.024] ], [[], [], []] ] ================================================ FILE: examples/success/user_array.json ================================================ [ { "ID": 7947402710862746952, "nickname": "bigger than bigger", "birthday": { "year": 1984, "month": 9, "day": 2, "type": "Jewish" }, "block_event": { "serial_number": 9097588792683265916, "administrator ID": 10720293540521355122, "date": { "year": 1970, "month": 12, "day": 31, "type": "Chinese" }, "description": "advertisement", "details": "most likely a troll" }, "dark_event": { "serial_number": 9876543210123456789, "administrator ID": 11223344556677889900, "date": { "year": 1970, "month": 12, "day": 31 }, "description": "promote", "details": "at least like a troll", "flags": null }, "alternate_history": [ null, { "serial_number": 1123581321345589, "administrator ID": 1123581321345589, "date": { "year": 1970, "month": 12, "day": 31 }, "description": "redacted", "details": "redacted", "flags": "x" } ], "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": 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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } }, { "ID": 994851, "nickname": "bigger than bigger", "birthday": { "year": 1984, "month": 9, "day": 2 }, "block_event": { "serial_number": 75350019499420, "administrator ID": 1072029354, "date": { "year": 1993, "month": 9, "day": 7 }, "description": "advertisement", "details": "hehe" }, "auxiliary": [ 3, [[4.0, 5.0], [9.2, 3.3]], true ] } ] ================================================ FILE: examples/success/user_array_compact.json ================================================ [{"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?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠"}}] ================================================ FILE: examples/success/user_map.json ================================================ { "First": { "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_event": { "serial_number": 9876543210123456789, "administrator ID": 11223344556677889900, "date": { "year": 1970, "month": 12, "day": 31 }, "description": "promote", "details": "at least like a troll", "flags": null }, "alternate_history": [ null, { "serial_number": 1123581321345589, "administrator ID": 1123581321345589, "date": { "year": 1970, "month": 12, "day": 31 }, "description": "redacted", "details": "redacted", "flags": "x" } ], "comment": "This field is not specified by our class; but it is silently skipped because the parse_mode of class `User` is not `strict`" }, "Second": { "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": { "Self description": "Can you handle characters above Basic Multilingual Plane?😄🎅 🎁🐱✈️🐘🔢𠁆𠝻𡎦𡌠", "Open ID": "something@somewhere.com", "Auth-Token": "45F13704-A60D-4D44-B161-89BAB88E528C" } } } ================================================ FILE: format.sh ================================================ #!/bin/sh cd $(dirname $0) GLOBIGNORE="include/staticjson/optional_support.hpp" clang-format -i --style=File autojsoncxx/*.hpp include/staticjson/*.hpp src/*.cpp test/*.cpp test/myarray.hpp ================================================ FILE: include/staticjson/basic.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include namespace staticjson { struct NonMobile { NonMobile() {} ~NonMobile() {} NonMobile(const NonMobile&) = delete; NonMobile(NonMobile&&) = delete; NonMobile& operator=(const NonMobile&) = delete; NonMobile& operator=(NonMobile&&) = delete; }; typedef unsigned int SizeType; // This class is not thread safe, so please set all values at startup or when single threaded. class GlobalConfig : private NonMobile { public: static GlobalConfig* getInstance() noexcept; SizeType getMemoryChunkSize() const noexcept { return memoryChunkSize; } void setMemoryChunkSize(SizeType value) noexcept { memoryChunkSize = value; } void setMaxLeaves(SizeType maxNum) noexcept { maxLeaves = maxNum; _isMaxLeavesSet = true; } void setMaxDepth(SizeType maxDep) noexcept { maxDepth = maxDep; _isMaxDepthSet = true; } SizeType getMaxDepth() const noexcept { return maxDepth; } SizeType getMaxLeaves() const noexcept { return maxLeaves; } bool isMaxLeavesSet() const noexcept { return _isMaxLeavesSet; } bool isMaxDepthSet() const noexcept { return _isMaxDepthSet; } void unsetMaxLeavesFlag() noexcept { _isMaxLeavesSet = false; maxLeaves = UINT_MAX; } void unsetMaxDepthFlag() noexcept { _isMaxDepthSet = false; maxDepth = UINT_MAX; } private: GlobalConfig() {} bool _isMaxLeavesSet = false; bool _isMaxDepthSet = false; SizeType maxLeaves = UINT_MAX; SizeType maxDepth = UINT_MAX; SizeType memoryChunkSize = 1000; }; class IHandler { public: IHandler() {} virtual ~IHandler(); virtual bool Null() = 0; virtual bool Bool(bool) = 0; virtual bool Int(int) = 0; virtual bool Uint(unsigned) = 0; virtual bool Int64(std::int64_t) = 0; virtual bool Uint64(std::uint64_t) = 0; virtual bool Double(double) = 0; virtual bool String(const char*, SizeType, bool) = 0; virtual bool StartObject() = 0; virtual bool Key(const char*, SizeType, bool) = 0; virtual bool EndObject(SizeType) = 0; virtual bool StartArray() = 0; virtual bool EndArray(SizeType) = 0; virtual bool RawNumber(const char*, SizeType, bool); virtual void prepare_for_reuse() = 0; }; using rapidjson::Document; using rapidjson::Value; typedef rapidjson::MemoryPoolAllocator<> MemoryPoolAllocator; class BaseHandler : public IHandler, private NonMobile { friend class NullableHandler; protected: std::unique_ptr the_error; bool parsed = false; protected: bool set_out_of_range(const char* actual_type); bool set_type_mismatch(const char* actual_type); virtual void reset() {} public: BaseHandler() {} virtual ~BaseHandler(); virtual std::string type_name() const = 0; virtual bool Null() override { return set_type_mismatch("null"); } virtual bool Bool(bool) override { return set_type_mismatch("bool"); } virtual bool Int(int) override { return set_type_mismatch("int"); } virtual bool Uint(unsigned) override { return set_type_mismatch("unsigned"); } virtual bool Int64(std::int64_t) override { return set_type_mismatch("int64_t"); } virtual bool Uint64(std::uint64_t) override { return set_type_mismatch("uint64_t"); } virtual bool Double(double) override { return set_type_mismatch("double"); } virtual bool String(const char*, SizeType, bool) override { return set_type_mismatch("string"); } virtual bool StartObject() override { return set_type_mismatch("object"); } virtual bool Key(const char*, SizeType, bool) override { return set_type_mismatch("object"); } virtual bool EndObject(SizeType) override { return set_type_mismatch("object"); } virtual bool StartArray() override { return set_type_mismatch("array"); } virtual bool EndArray(SizeType) override { return set_type_mismatch("array"); } virtual bool has_error() const { return bool(the_error); } virtual bool reap_error(ErrorStack& errs) { if (!the_error) return false; errs.push(the_error.release()); return true; } bool is_parsed() const { return parsed; } void prepare_for_reuse() override { the_error.reset(); parsed = false; reset(); } virtual bool write(IHandler* output) const = 0; virtual void generate_schema(Value& output, MemoryPoolAllocator& alloc) const = 0; }; struct Flags { static const unsigned Default = 0x0, AllowDuplicateKey = 0x1, Optional = 0x2, IgnoreRead = 0x4, IgnoreWrite = 0x8, DisallowUnknownKey = 0x10; }; // Forward declaration template class Handler; namespace mempool { [[noreturn]] void throw_bad_alloc(); rapidjson::CrtAllocator& get_crt_allocator() noexcept; template class PooledAllocator { private: MemoryPoolAllocator* pool; template friend class PooledAllocator; public: using value_type = T; using propagate_on_container_copy_assignment = std::true_type; using propagate_on_container_move_assignment = std::true_type; using propagate_on_container_swap = std::true_type; explicit PooledAllocator(MemoryPoolAllocator* pool) noexcept : pool(pool) {} template PooledAllocator(const PooledAllocator& other) noexcept : pool(other.pool) { } template bool operator==(const PooledAllocator& other) const noexcept { return pool == other.pool; } template bool operator!=(const PooledAllocator& other) const noexcept { return pool != other.pool; } T* allocate(const size_t n) const { if (!n) { return nullptr; } auto ptr = static_cast(pool->Malloc(n * sizeof(T))); if (!ptr) { throw_bad_alloc(); } return ptr; } void deallocate(T* const p, size_t) const noexcept { if (p) { pool->Free(p); } } }; template using Map = std::map, PooledAllocator>>; template using Stack = std::stack>>; using String = std::basic_string, PooledAllocator>; inline std::string to_std_string(const String& str) { return {str.data(), str.size()}; } template struct PooledDeleter { void operator()(T* ptr) const noexcept { if (!ptr) { return; } ptr->~T(); static_assert(!MemoryPoolAllocator::kNeedFree, "MemoryPoolAllocator must not need freeing"); } }; template using UniquePtr = std::unique_ptr>; template T* pooled_new(MemoryPoolAllocator& pool, Args&&... args) { auto storage = pool.Malloc(sizeof(T)); static_assert(!MemoryPoolAllocator::kNeedFree, "MemoryPoolAllocator must not need freeing or we will need to handle " "potential exceptions in the construction of T"); new (storage) T(std::forward(args)...); return static_cast(storage); } } class ObjectHandler : public BaseHandler { protected: struct FlaggedHandler { mempool::UniquePtr handler; unsigned flags; }; protected: MemoryPoolAllocator memory_pool_allocator; mempool::Map internals; FlaggedHandler* current = nullptr; mempool::String current_name; int depth = 0; unsigned flags = Flags::Default; unsigned int jsonDepth = 0; // true if last symbol is '[', otherwise false bool lastLeafStat = false; SizeType totalLeaves = 0; // save the number of object or array mempool::Stack leavesStack; protected: void postinit(); bool precheck(const char* type); bool postcheck(bool success); void set_missing_required(const std::string& name); void add_handler(mempool::String&&, FlaggedHandler&&); void reset() override; private: bool StartCheckMaxDepthMaxLeaves(bool isArray); bool EndCheckMaxDepthMaxLeaves(SizeType sz, bool isArray); template void add_property(mempool::String name, T* pointer, unsigned flags_ = Flags::Default) { FlaggedHandler fh; fh.handler.reset(mempool::pooled_new>(memory_pool_allocator, pointer)); fh.flags = flags_; add_handler(std::move(name), std::move(fh)); } public: ObjectHandler(); ~ObjectHandler(); std::string type_name() const override; virtual bool Null() override; virtual bool Bool(bool) override; virtual bool Int(int) override; virtual bool Uint(unsigned) override; virtual bool Int64(std::int64_t) override; virtual bool Uint64(std::uint64_t) override; virtual bool Double(double) override; virtual bool String(const char*, SizeType, bool) override; virtual bool StartObject() override; virtual bool Key(const char*, SizeType, bool) override; virtual bool EndObject(SizeType) override; virtual bool StartArray() override; virtual bool EndArray(SizeType) override; virtual bool reap_error(ErrorStack&) override; virtual bool write(IHandler* output) const override; virtual void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override; unsigned get_flags() const { return flags; } void set_flags(unsigned f) { flags = f; } const MemoryPoolAllocator& get_memory_pool() const noexcept { return memory_pool_allocator; } template void add_property(const std::string& name, T* pointer, unsigned flags_ = Flags::Default) { add_property(mempool::String(name.data(), name.size(), mempool::String::allocator_type(&memory_pool_allocator)), pointer, flags_); } template void add_property(const char* name, T* pointer, unsigned flags_ = Flags::Default) { add_property(mempool::String(name, mempool::String::allocator_type(&memory_pool_allocator)), pointer, flags_); } }; template struct Converter { typedef T shadow_type; static std::unique_ptr from_shadow(const shadow_type& shadow, T& value) { (void)shadow; (void)value; return nullptr; } static void to_shadow(const T& value, shadow_type& shadow) { (void)shadow; (void)value; } static std::string type_name() { return "T"; } static constexpr bool has_specialized_type_name = false; }; template void init(T* t, ObjectHandler* h) { t->staticjson_init(h); } template class ObjectTypeHandler : public ObjectHandler { public: explicit ObjectTypeHandler(T* t) { init(t, this); postinit(); } }; template class ConversionHandler : public BaseHandler { private: typedef typename Converter::shadow_type shadow_type; typedef Handler internal_type; private: shadow_type shadow; internal_type internal; T* m_value; protected: bool postprocess(bool success) { if (!success) { return false; } if (!internal.is_parsed()) return true; this->parsed = true; auto err = Converter::from_shadow(shadow, *m_value); if (err) { this->the_error.swap(err); return false; } return true; } void reset() override { shadow = shadow_type(); internal.prepare_for_reuse(); } public: explicit ConversionHandler(T* t) : shadow(), internal(&shadow), m_value(t) {} std::string type_name() const override { // if (Converter::has_specialized_type_name) // return Converter::type_name(); return internal.type_name(); } virtual bool Null() override { return postprocess(internal.Null()); } virtual bool Bool(bool b) override { return postprocess(internal.Bool(b)); } virtual bool Int(int i) override { return postprocess(internal.Int(i)); } virtual bool Uint(unsigned u) override { return postprocess(internal.Uint(u)); } virtual bool Int64(std::int64_t i) override { return postprocess(internal.Int64(i)); } virtual bool Uint64(std::uint64_t u) override { return postprocess(internal.Uint64(u)); } virtual bool Double(double d) override { return postprocess(internal.Double(d)); } virtual bool String(const char* str, SizeType size, bool copy) override { return postprocess(internal.String(str, size, copy)); } virtual bool StartObject() override { return postprocess(internal.StartObject()); } virtual bool Key(const char* str, SizeType size, bool copy) override { return postprocess(internal.Key(str, size, copy)); } virtual bool EndObject(SizeType sz) override { return postprocess(internal.EndObject(sz)); } virtual bool StartArray() override { return postprocess(internal.StartArray()); } virtual bool EndArray(SizeType sz) override { return postprocess(internal.EndArray(sz)); } virtual bool has_error() const override { return BaseHandler::has_error() || internal.has_error(); } bool reap_error(ErrorStack& errs) override { return BaseHandler::reap_error(errs) || internal.reap_error(errs); } virtual bool write(IHandler* output) const override { Converter::to_shadow(*m_value, const_cast(shadow)); return internal.write(output); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { return internal.generate_schema(output, alloc); } }; namespace helper { template class DispatchHandler; template class DispatchHandler : public ::staticjson::ObjectTypeHandler { public: explicit DispatchHandler(T* t) : ::staticjson::ObjectTypeHandler(t) {} }; template class DispatchHandler : public ::staticjson::ConversionHandler { public: explicit DispatchHandler(T* t) : ::staticjson::ConversionHandler(t) {} }; } template class Handler : public helper::DispatchHandler::shadow_type, T>::value> { public: typedef helper::DispatchHandler::shadow_type, T>::value> base_type; explicit Handler(T* t) : base_type(t) {} }; } ================================================ FILE: include/staticjson/document.hpp ================================================ #pragma once #include #include #include namespace staticjson { class JSONHandler : public BaseHandler { protected: static const int MAX_DEPTH = 32; std::vector m_stack; Value* m_value; MemoryPoolAllocator* m_alloc; private: bool stack_push(); void stack_pop(); Value& stack_top(); bool postprocess(); bool set_corrupted_dom(); public: explicit JSONHandler(Value* v, MemoryPoolAllocator* a); std::string type_name() const override; virtual bool Null() override; virtual bool Bool(bool) override; virtual bool Int(int) override; virtual bool Uint(unsigned) override; virtual bool Int64(std::int64_t) override; virtual bool Uint64(std::uint64_t) override; virtual bool Double(double) override; virtual bool String(const char*, SizeType, bool) override; virtual bool StartObject() override; virtual bool Key(const char*, SizeType, bool) override; virtual bool EndObject(SizeType) override; virtual bool StartArray() override; virtual bool EndArray(SizeType) override; virtual bool write(IHandler* output) const override; virtual void reset() override; void reset(MemoryPoolAllocator* a); void generate_schema(Value& output, MemoryPoolAllocator&) const override { output.SetObject(); } }; template <> class Handler : public JSONHandler { public: explicit Handler(Document* h) : JSONHandler(h, &h->GetAllocator()) {} virtual void reset() override { JSONHandler::reset(&(static_cast(this->m_value)->GetAllocator())); } }; namespace nonpublic { bool write_value(const Value& v, BaseHandler* out, ParseStatus* status); bool read_value(Value* v, MemoryPoolAllocator* alloc, const BaseHandler* input, ParseStatus* status); } template bool from_json_value(const Value& v, T* t, ParseStatus* status) { Handler h(t); return nonpublic::write_value(v, &h, status); } template bool from_json_document(const Document& d, T* t, ParseStatus* status) // for consistency in API { return from_json_value(d, t, status); } template bool to_json_value(Value* v, MemoryPoolAllocator* alloc, const T& t, ParseStatus* status) { Handler h(const_cast(&t)); return nonpublic::read_value(v, alloc, &h, status); } template bool to_json_document(Document* d, const T& t, ParseStatus* status) { return to_json_value(d, &d->GetAllocator(), t, status); } } ================================================ FILE: include/staticjson/enum.hpp ================================================ #pragma once #include #include #include #include namespace staticjson { template class EnumHandler : public BaseHandler { private: Enum* m_value; static const std::vector>& get_mapping() { return Derived::get_mapping(); }; public: explicit EnumHandler(Enum* value) : m_value(value) {} bool String(const char* str, SizeType sz, bool) override { const auto& mapping = get_mapping(); for (const std::pair& pair : mapping) { if (pair.first.size() == sz && memcmp(pair.first.data(), str, sz) == 0) { *m_value = pair.second; this->parsed = true; return true; } } the_error.reset(new error::InvalidEnumError(std::string(str, str + sz))); return false; } bool write(IHandler* output) const override { const auto& mapping = get_mapping(); for (const std::pair& pair : mapping) { if (*m_value == pair.second) { output->String(pair.first.data(), static_cast(pair.first.size()), false); return true; } } return false; } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("string"), alloc); Value enumerations(rapidjson::kArrayType); const auto& mapping = get_mapping(); for (const std::pair& pair : mapping) { enumerations.PushBack(rapidjson::StringRef(pair.first.data(), pair.first.size()), alloc); } output.AddMember(rapidjson::StringRef("enum"), enumerations, alloc); } }; } #define STATICJSON_DECLARE_ENUM(type, ...) \ namespace staticjson \ { \ template <> \ class Handler : public EnumHandler> \ { \ public: \ explicit Handler(type* value) : EnumHandler>(value) {} \ std::string type_name() const override { return #type; } \ static const std::vector>& get_mapping() \ { \ static std::vector> mapping{__VA_ARGS__}; \ return mapping; \ } \ }; \ } ================================================ FILE: include/staticjson/error.hpp ================================================ #pragma once #include #include #include #include #include #include namespace staticjson { std::string quote(const std::string& str); class ErrorStack; class ErrorBase; namespace error { namespace internal { class error_stack_const_iterator; } using staticjson::ErrorBase; using staticjson::ErrorStack; } class ErrorBase { protected: explicit ErrorBase() : next(0) {} private: ErrorBase* next; friend class ErrorStack; friend class error::internal::error_stack_const_iterator; public: virtual int type() const = 0; virtual bool is_intermediate() const { return false; } virtual ~ErrorBase() {} virtual std::string description() const = 0; }; namespace error { typedef int error_type; static const error_type SUCCESS = 0, OBJECT_MEMBER = 1, ARRAY_ELEMENT = 2, MISSING_REQUIRED = 3, TYPE_MISMATCH = 4, NUMBER_OUT_OF_RANGE = 5, ARRAY_LENGTH_MISMATCH = 6, UNKNOWN_FIELD = 7, DUPLICATE_KEYS = 8, CORRUPTED_DOM = 9, TOO_DEEP_RECURSION = 10, INVALID_ENUM = 11, TOO_MANY_LEAVES = 12, CUSTOM = -1; class Success : public ErrorBase { public: explicit Success() {} std::string description() const; error_type type() const { return SUCCESS; } }; class IntermediateError : public ErrorBase { public: bool is_intermediate() const { return true; } }; class ObjectMemberError : public IntermediateError { private: std::string m_member_name; public: explicit ObjectMemberError(std::string memberName) { m_member_name.swap(memberName); } const std::string& member_name() const { return m_member_name; } std::string description() const; error_type type() const { return OBJECT_MEMBER; } }; class ArrayElementError : public IntermediateError { private: std::size_t m_index; public: explicit ArrayElementError(std::size_t idx) : m_index(idx) {} std::size_t index() const { return m_index; } std::string description() const; error_type type() const { return ARRAY_ELEMENT; } }; class RequiredFieldMissingError : public ErrorBase { private: std::vector m_missing_members; public: explicit RequiredFieldMissingError() {} std::vector& missing_members() { return m_missing_members; }; const std::vector& missing_members() const { return m_missing_members; } std::string description() const; error_type type() const { return MISSING_REQUIRED; } }; class TypeMismatchError : public ErrorBase { private: std::string m_expected_type; std::string m_actual_type; public: explicit TypeMismatchError(std::string expectedType, std::string actualType) { m_expected_type.swap(expectedType); m_actual_type.swap(actualType); } const std::string& expected_type() const { return m_expected_type; } const std::string& actual_type() const { return m_actual_type; } std::string description() const; error_type type() const { return TYPE_MISMATCH; } }; class RecursionTooDeepError : public ErrorBase { std::string description() const override; error_type type() const override { return TOO_DEEP_RECURSION; } }; class TooManyLeavesError : public ErrorBase { std::string description() const override; error_type type() const override { return TOO_MANY_LEAVES; } }; class NumberOutOfRangeError : public ErrorBase { std::string m_expected_type; std::string m_actual_type; public: explicit NumberOutOfRangeError(std::string expectedType, std::string actualType) { m_expected_type.swap(expectedType); m_actual_type.swap(actualType); } const std::string& expected_type() const { return m_expected_type; } const std::string& actual_type() const { return m_actual_type; } std::string description() const; error_type type() const { return NUMBER_OUT_OF_RANGE; } }; class DuplicateKeyError : public ErrorBase { private: std::string key_name; public: explicit DuplicateKeyError(std::string name) { key_name.swap(name); } const std::string& key() const { return key_name; } error_type type() const { return DUPLICATE_KEYS; } std::string description() const; }; class UnknownFieldError : public ErrorBase { private: std::string m_name; public: explicit UnknownFieldError(const char* name, std::size_t length) : m_name(name, length) {} const std::string& field_name() const { return m_name; } error_type type() const { return UNKNOWN_FIELD; } std::string description() const; }; class CorruptedDOMError : public ErrorBase { public: std::string description() const; error_type type() const { return CORRUPTED_DOM; } }; class ArrayLengthMismatchError : public ErrorBase { public: std::string description() const; error_type type() const { return ARRAY_LENGTH_MISMATCH; } }; class InvalidEnumError : public ErrorBase { private: std::string m_name; public: explicit InvalidEnumError(std::string name) { m_name.swap(name); } std::string description() const; error_type type() const { return INVALID_ENUM; } }; class CustomError : public ErrorBase { private: std::string m_message; public: explicit CustomError(std::string message) { m_message.swap(message); } std::string description() const; error_type type() const { return CUSTOM; } }; namespace internal { class error_stack_const_iterator { private: const ErrorBase* e; public: using iterator_category = std::forward_iterator_tag; using value_type = const ErrorBase; using difference_type = ptrdiff_t; using pointer = const ErrorBase*; using reference = const ErrorBase&; public: explicit error_stack_const_iterator(const ErrorBase* p) : e(p) {} reference operator*() const { return *e; } pointer operator->() const { return e; } error_stack_const_iterator& operator++() { e = e->next; return *this; } bool operator==(error_stack_const_iterator that) const { return e == that.e; } bool operator!=(error_stack_const_iterator that) const { return e != that.e; } }; } } class ErrorStack { private: ErrorBase* head; std::size_t m_size; ErrorStack(const ErrorStack&); ErrorStack& operator=(const ErrorStack&); public: typedef error::internal::error_stack_const_iterator const_iterator; explicit ErrorStack() : head(0), m_size(0) {} const_iterator begin() const { return const_iterator(head); } const_iterator end() const { return const_iterator(0); } // This will take the ownership of e // Requires it to be dynamically allocated void push(ErrorBase* e) { if (e) { e->next = head; head = e; ++m_size; } } // The caller will take the responsibility of deleting the returned pointer // Returns NULL when empty ErrorBase* pop() { if (head) { ErrorBase* result = head; head = head->next; --m_size; return result; } return 0; } bool empty() const { return head == 0; } explicit operator bool() const { return !empty(); } bool operator!() const { return empty(); } std::size_t size() const { return m_size; } ~ErrorStack() { while (head) { ErrorBase* next = head->next; delete head; head = next; } } void swap(ErrorStack& other) noexcept { std::swap(head, other.head); std::swap(m_size, other.m_size); } ErrorStack(ErrorStack&& other) : head(other.head), m_size(other.m_size) { other.head = 0; other.m_size = 0; } ErrorStack& operator==(ErrorStack&& other) { swap(other); return *this; } }; // For argument dependent lookup inline void swap(ErrorStack& s1, ErrorStack& s2) { s1.swap(s2); } class ParseStatus { private: ErrorStack m_stack; std::size_t m_offset; int m_code; public: explicit ParseStatus() : m_stack(), m_offset(), m_code() {} void set_result(int err, std::size_t off) { m_code = err; m_offset = off; } int error_code() const { return m_code; } std::size_t offset() const { return m_offset; } std::string short_description() const; ErrorStack& error_stack() { return m_stack; } const ErrorStack& error_stack() const { return m_stack; } typedef ErrorStack::const_iterator const_iterator; const_iterator begin() const { return m_stack.begin(); } const_iterator end() const { return m_stack.end(); } bool has_error() const { return m_code != 0 || !m_stack.empty(); } std::string description() const; void swap(ParseStatus& other) noexcept { std::swap(m_code, other.m_code); std::swap(m_offset, other.m_offset); m_stack.swap(other.m_stack); } bool operator!() const { return has_error(); } explicit operator bool() const { return !has_error(); } ParseStatus(ParseStatus&& other) noexcept : m_stack(), m_offset(), m_code() { swap(other); } ParseStatus& operator==(ParseStatus&& other) noexcept { swap(other); return *this; } }; // For argument dependent lookup inline void swap(ParseStatus& r1, ParseStatus& r2) { r1.swap(r2); } } ================================================ FILE: include/staticjson/forward_declarations.hpp ================================================ #pragma once namespace staticjson { typedef unsigned int SizeType; class IHandler; class BaseHandler; class ObjectHandler; template class Handler; } ================================================ FILE: include/staticjson/io.hpp ================================================ #pragma once #include #include #include namespace staticjson { namespace nonpublic { bool parse_json_string(const char* str, BaseHandler* handler, ParseStatus* status); bool parse_json_file(std::FILE* fp, BaseHandler* handler, ParseStatus* status); std::string serialize_json_string(const BaseHandler* handler); bool serialize_json_file(std::FILE* fp, const BaseHandler* handler); std::string serialize_pretty_json_string(const BaseHandler* handler); bool serialize_pretty_json_file(std::FILE* fp, const BaseHandler* handler); struct FileGuard : private NonMobile { std::FILE* fp; explicit FileGuard(std::FILE* fp) : fp(fp) {} ~FileGuard() { if (fp) std::fclose(fp); } }; } template inline bool from_json_string(const char* str, T* value, ParseStatus* status) { Handler h(value); return nonpublic::parse_json_string(str, &h, status); } template inline bool from_json_file(std::FILE* fp, T* value, ParseStatus* status) { Handler h(value); return nonpublic::parse_json_file(fp, &h, status); } template inline bool from_json_file(const char* filename, T* value, ParseStatus* status) { nonpublic::FileGuard fg(std::fopen(filename, "r")); return from_json_file(fg.fp, value, status); } template inline bool from_json_file(const std::string& filename, T* value, ParseStatus* status) { return from_json_file(filename.c_str(), value, status); } template inline std::string to_json_string(const T& value) { Handler h(const_cast(&value)); return nonpublic::serialize_json_string(&h); } template inline bool to_json_file(std::FILE* fp, const T& value) { Handler h(const_cast(&value)); return nonpublic::serialize_json_file(fp, &h); } template inline bool to_json_file(const char* filename, const T& value) { nonpublic::FileGuard fg(std::fopen(filename, "wb")); return to_json_file(fg.fp, value); } template inline bool to_json_file(const std::string& filename, const T& value) { return to_json_file(filename.c_str(), value); } template inline std::string to_pretty_json_string(const T& value) { Handler h(const_cast(&value)); return nonpublic::serialize_pretty_json_string(&h); } template inline bool to_pretty_json_file(std::FILE* fp, const T& value) { Handler h(const_cast(&value)); return nonpublic::serialize_pretty_json_file(fp, &h); } template inline bool to_pretty_json_file(const char* filename, const T& value) { nonpublic::FileGuard fg(std::fopen(filename, "wb")); return to_pretty_json_file(fg.fp, value); } template inline bool to_pretty_json_file(const std::string& filename, const T& value) { return to_pretty_json_file(filename.c_str(), value); } template inline Document export_json_schema(T* value, Document::AllocatorType* allocator = nullptr) { Handler h(value); Document d; h.generate_schema(d, allocator ? *allocator : d.GetAllocator()); return d; } } ================================================ FILE: include/staticjson/optional_support.hpp ================================================ #pragma once #include "stl_types.hpp" #ifdef __has_include #if __has_include() #include namespace staticjson { template using optional = std::optional; using std::nullopt; } #elif __has_include() #include namespace staticjson { template using optional = std::experimental::optional; using std::experimental::nullopt; } #else #error "Missing " #endif #else #error "Missing " #endif namespace staticjson { template class Handler> : public BaseHandler { public: using ElementType = T; protected: mutable optional* m_value; mutable optional> internal_handler; int depth = 0; public: explicit Handler(optional* value) : m_value(value) {} protected: void initialize() { if (!internal_handler) { m_value->emplace(); internal_handler.emplace(&(**m_value)); } } void reset() override { depth = 0; internal_handler = nullopt; *m_value = nullopt; } bool postcheck(bool success) { if (success) this->parsed = internal_handler->is_parsed(); return success; } public: bool Null() override { if (depth == 0) { *m_value = nullopt; this->parsed = true; return true; } else { initialize(); return postcheck(internal_handler->Null()); } } bool write(IHandler* out) const override { if (!m_value || !(*m_value)) { return out->Null(); } if (!internal_handler) { internal_handler.emplace(&(**m_value)); } return internal_handler->write(out); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { const_cast>*>(this)->initialize(); output.SetObject(); Value anyOf(rapidjson::kArrayType); Value nullDescriptor(rapidjson::kObjectType); nullDescriptor.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("null"), alloc); Value descriptor; internal_handler->generate_schema(descriptor, alloc); anyOf.PushBack(nullDescriptor, alloc); anyOf.PushBack(descriptor, alloc); output.AddMember(rapidjson::StringRef("anyOf"), anyOf, alloc); } bool Bool(bool b) override { initialize(); return postcheck(internal_handler->Bool(b)); } bool Int(int i) override { initialize(); return postcheck(internal_handler->Int(i)); } bool Uint(unsigned i) override { initialize(); return postcheck(internal_handler->Uint(i)); } bool Int64(std::int64_t i) override { initialize(); return postcheck(internal_handler->Int64(i)); } bool Uint64(std::uint64_t i) override { initialize(); return postcheck(internal_handler->Uint64(i)); } bool Double(double i) override { initialize(); return postcheck(internal_handler->Double(i)); } bool String(const char* str, SizeType len, bool copy) override { initialize(); return postcheck(internal_handler->String(str, len, copy)); } bool Key(const char* str, SizeType len, bool copy) override { initialize(); return postcheck(internal_handler->Key(str, len, copy)); } bool StartObject() override { initialize(); ++depth; return internal_handler->StartObject(); } bool EndObject(SizeType len) override { initialize(); --depth; return postcheck(internal_handler->EndObject(len)); } bool StartArray() override { initialize(); ++depth; return postcheck(internal_handler->StartArray()); } bool EndArray(SizeType len) override { initialize(); --depth; return postcheck(internal_handler->EndArray(len)); } bool has_error() const override { return internal_handler && internal_handler->has_error(); } bool reap_error(ErrorStack& stk) override { return internal_handler && internal_handler->reap_error(stk); } std::string type_name() const override { if (this->internal_handler) { return "std::optional<" + this->internal_handler->type_name() + ">"; } return "std::optional"; } }; } ================================================ FILE: include/staticjson/primitive_types.hpp ================================================ #pragma once #include #include #include #include namespace staticjson { template class IntegerHandler : public BaseHandler { static_assert(std::is_arithmetic::value, "Only arithmetic types are allowed"); protected: IntType* m_value; template static constexpr typename std::enable_if::value, bool>::type is_out_of_range(AnotherIntType a) { typedef typename std::common_type::type CommonType; typedef typename std::numeric_limits this_limits; typedef typename std::numeric_limits that_limits; // The extra logic related to this_limits::min/max allows the compiler to // short circuit this check at compile time. For instance, a `uint32_t` // will NEVER be out of range for an `int64_t` return ((this_limits::is_signed == that_limits::is_signed) ? ((CommonType(this_limits::min()) > CommonType(a) || CommonType(this_limits::max()) < CommonType(a))) : (this_limits::is_signed) ? (CommonType(this_limits::max()) < CommonType(a)) : (a < 0 || CommonType(a) > CommonType(this_limits::max()))); } template static constexpr typename std::enable_if::value, bool>::type is_out_of_range(FloatType f) { return static_cast(static_cast(f)) != f; } template bool receive(ReceiveNumType r, const char* actual_type) { if (is_out_of_range(r)) return set_out_of_range(actual_type); *m_value = static_cast(r); this->parsed = true; return true; } public: explicit IntegerHandler(IntType* value) : m_value(value) {} bool Int(int i) override { return receive(i, "int"); } bool Uint(unsigned i) override { return receive(i, "unsigned int"); } bool Int64(std::int64_t i) override { return receive(i, "std::int64_t"); } bool Uint64(std::uint64_t i) override { return receive(i, "std::uint64_t"); } bool Double(double d) override { return receive(d, "double"); } bool write(IHandler* output) const override { if (std::numeric_limits::is_signed) { return output->Int64(*m_value); } else { return output->Uint64(*m_value); } } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("integer"), alloc); Value minimum, maximum; if (std::numeric_limits::is_signed) { minimum.SetInt64(std::numeric_limits::min()); maximum.SetInt64(std::numeric_limits::max()); } else { minimum.SetUint64(std::numeric_limits::min()); maximum.SetUint64(std::numeric_limits::max()); } output.AddMember(rapidjson::StringRef("minimum"), minimum, alloc); output.AddMember(rapidjson::StringRef("maximum"), maximum, alloc); } }; template <> class Handler : public BaseHandler { public: explicit Handler(std::nullptr_t*) {} bool Null() override { this->parsed = true; return true; } std::string type_name() const override { return "null"; } bool write(IHandler* output) const override { return output->Null(); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("null"), alloc); } }; template <> class Handler : public BaseHandler { private: bool* m_value; public: explicit Handler(bool* value) : m_value(value) {} bool Bool(bool v) override { *m_value = v; this->parsed = true; return true; } std::string type_name() const override { return "bool"; } bool write(IHandler* output) const override { return output->Bool(*m_value); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc); } }; template <> class Handler : public IntegerHandler { public: explicit Handler(int* i) : IntegerHandler(i) {} std::string type_name() const override { return "int"; } bool write(IHandler* output) const override { return output->Int(*m_value); } }; template <> class Handler : public IntegerHandler { public: explicit Handler(unsigned* i) : IntegerHandler(i) {} std::string type_name() const override { return "unsigned int"; } bool write(IHandler* output) const override { return output->Uint(*m_value); } }; template <> class Handler : public IntegerHandler { public: explicit Handler(long* i) : IntegerHandler(i) {} std::string type_name() const override { return "long"; } }; template <> class Handler : public IntegerHandler { public: explicit Handler(unsigned long* i) : IntegerHandler(i) {} std::string type_name() const override { return "unsigned long"; } }; template <> class Handler : public IntegerHandler { public: explicit Handler(long long* i) : IntegerHandler(i) {} std::string type_name() const override { return "long long"; } }; template <> class Handler : public IntegerHandler { public: explicit Handler(unsigned long long* i) : IntegerHandler(i) {} std::string type_name() const override { return "unsigned long long"; } }; // char is an alias for bool to work around the stupid `std::vector` template <> class Handler : public BaseHandler { private: char* m_value; public: explicit Handler(char* i) : m_value(i) {} std::string type_name() const override { return "bool"; } bool Bool(bool v) override { *this->m_value = v; this->parsed = true; return true; } bool write(IHandler* out) const override { return out->Bool(*m_value != 0); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc); } }; template <> class Handler : public BaseHandler { private: double* m_value; public: explicit Handler(double* v) : m_value(v) {} bool Int(int i) override { *m_value = i; this->parsed = true; return true; } bool Uint(unsigned i) override { *m_value = i; this->parsed = true; return true; } bool Int64(std::int64_t i) override { *m_value = static_cast(i); if (static_cast(*m_value) != i) return set_out_of_range("std::int64_t"); this->parsed = true; return true; } bool Uint64(std::uint64_t i) override { *m_value = static_cast(i); if (static_cast(*m_value) != i) return set_out_of_range("std::uint64_t"); this->parsed = true; return true; } bool Double(double d) override { *m_value = d; this->parsed = true; return true; } std::string type_name() const override { return "double"; } bool write(IHandler* out) const override { return out->Double(*m_value); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc); } }; template <> class Handler : public BaseHandler { private: float* m_value; public: explicit Handler(float* v) : m_value(v) {} bool Int(int i) override { *m_value = static_cast(i); if (static_cast(*m_value) != i) return set_out_of_range("int"); this->parsed = true; return true; } bool Uint(unsigned i) override { *m_value = static_cast(i); if (static_cast(*m_value) != i) return set_out_of_range("unsigned int"); this->parsed = true; return true; } bool Int64(std::int64_t i) override { *m_value = static_cast(i); if (static_cast(*m_value) != i) return set_out_of_range("std::int64_t"); this->parsed = true; return true; } bool Uint64(std::uint64_t i) override { *m_value = static_cast(i); if (static_cast(*m_value) != i) return set_out_of_range("std::uint64_t"); this->parsed = true; return true; } bool Double(double d) override { *m_value = static_cast(d); this->parsed = true; return true; } std::string type_name() const override { return "float"; } bool write(IHandler* out) const override { return out->Double(*m_value); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc); } }; template <> class Handler : public BaseHandler { private: std::string* m_value; public: explicit Handler(std::string* v) : m_value(v) {} bool String(const char* str, SizeType length, bool) override { m_value->assign(str, length); this->parsed = true; return true; } std::string type_name() const override { return "string"; } bool write(IHandler* out) const override { return out->String(m_value->data(), SizeType(m_value->size()), true); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("string"), alloc); } }; } ================================================ FILE: include/staticjson/staticjson.hpp ================================================ #pragma once #include #include #include #include #include #include ================================================ FILE: include/staticjson/stl_types.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include namespace staticjson { template class ArrayHandler : public BaseHandler { public: typedef typename ArrayType::value_type ElementType; protected: ElementType element; Handler internal; ArrayType* m_value; int depth = 0; protected: void set_element_error() { the_error.reset(new error::ArrayElementError(m_value->size())); } bool precheck(const char* type) { if (depth <= 0) { the_error.reset(new error::TypeMismatchError(type_name(), type)); return false; } return true; } bool postcheck(bool success) { if (!success) { set_element_error(); return false; } if (internal.is_parsed()) { m_value->emplace_back(std::move(element)); element = ElementType(); internal.prepare_for_reuse(); } return true; } void reset() override { element = ElementType(); internal.prepare_for_reuse(); depth = 0; } public: explicit ArrayHandler(ArrayType* value) : element(), internal(&element), m_value(value) {} bool Null() override { return precheck("null") && postcheck(internal.Null()); } bool Bool(bool b) override { return precheck("bool") && postcheck(internal.Bool(b)); } bool Int(int i) override { return precheck("int") && postcheck(internal.Int(i)); } bool Uint(unsigned i) override { return precheck("unsigned") && postcheck(internal.Uint(i)); } bool Int64(std::int64_t i) override { return precheck("int64_t") && postcheck(internal.Int64(i)); } bool Uint64(std::uint64_t i) override { return precheck("uint64_t") && postcheck(internal.Uint64(i)); } bool Double(double d) override { return precheck("double") && postcheck(internal.Double(d)); } bool String(const char* str, SizeType length, bool copy) override { return precheck("string") && postcheck(internal.String(str, length, copy)); } bool Key(const char* str, SizeType length, bool copy) override { return precheck("object") && postcheck(internal.Key(str, length, copy)); } bool StartObject() override { return precheck("object") && postcheck(internal.StartObject()); } bool EndObject(SizeType length) override { return precheck("object") && postcheck(internal.EndObject(length)); } bool StartArray() override { ++depth; if (depth > 1) return postcheck(internal.StartArray()); else m_value->clear(); return true; } bool EndArray(SizeType length) override { --depth; // When depth >= 1, this event should be forwarded to the element if (depth > 0) return postcheck(internal.EndArray(length)); this->parsed = true; return true; } bool reap_error(ErrorStack& stk) override { if (!the_error) return false; stk.push(the_error.release()); internal.reap_error(stk); return true; } bool write(IHandler* output) const override { if (!output->StartArray()) return false; for (auto&& e : *m_value) { Handler h(&e); if (!h.write(output)) return false; } return output->EndArray(static_cast(m_value->size())); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("array"), alloc); Value items; internal.generate_schema(items, alloc); output.AddMember(rapidjson::StringRef("items"), items, alloc); } const Handler& get_internal_handler() const noexcept { return internal; } }; template class Handler> : public ArrayHandler> { public: explicit Handler(std::vector* value) : ArrayHandler>(value) {} std::string type_name() const override { return "std::vector<" + this->internal.type_name() + ">"; } }; template class Handler> : public ArrayHandler> { public: explicit Handler(std::deque* value) : ArrayHandler>(value) {} std::string type_name() const override { return "std::deque<" + this->internal.type_name() + ">"; } }; template class Handler> : public ArrayHandler> { public: explicit Handler(std::list* value) : ArrayHandler>(value) {} std::string type_name() const override { return "std::list<" + this->internal.type_name() + ">"; } }; template class Handler> : public BaseHandler { protected: T element; Handler internal; std::array* m_value; size_t count = 0; int depth = 0; protected: void set_element_error() { the_error.reset(new error::ArrayElementError(count)); } void set_length_error() { the_error.reset(new error::ArrayLengthMismatchError()); } bool precheck(const char* type) { if (depth <= 0) { the_error.reset(new error::TypeMismatchError(type_name(), type)); return false; } return true; } bool postcheck(bool success) { if (!success) { set_element_error(); return false; } if (internal.is_parsed()) { if (count >= N) { set_length_error(); return false; } (*m_value)[count] = std::move(element); ++count; element = T(); internal.prepare_for_reuse(); } return true; } void reset() override { element = T(); internal.prepare_for_reuse(); depth = 0; count = 0; } public: explicit Handler(std::array* value) : element(), internal(&element), m_value(value) {} bool Null() override { return precheck("null") && postcheck(internal.Null()); } bool Bool(bool b) override { return precheck("bool") && postcheck(internal.Bool(b)); } bool Int(int i) override { return precheck("int") && postcheck(internal.Int(i)); } bool Uint(unsigned i) override { return precheck("unsigned") && postcheck(internal.Uint(i)); } bool Int64(std::int64_t i) override { return precheck("int64_t") && postcheck(internal.Int64(i)); } bool Uint64(std::uint64_t i) override { return precheck("uint64_t") && postcheck(internal.Uint64(i)); } bool Double(double d) override { return precheck("double") && postcheck(internal.Double(d)); } bool String(const char* str, SizeType length, bool copy) override { return precheck("string") && postcheck(internal.String(str, length, copy)); } bool Key(const char* str, SizeType length, bool copy) override { return precheck("object") && postcheck(internal.Key(str, length, copy)); } bool StartObject() override { return precheck("object") && postcheck(internal.StartObject()); } bool EndObject(SizeType length) override { return precheck("object") && postcheck(internal.EndObject(length)); } bool StartArray() override { ++depth; if (depth > 1) return postcheck(internal.StartArray()); return true; } bool EndArray(SizeType length) override { --depth; // When depth >= 1, this event should be forwarded to the element if (depth > 0) return postcheck(internal.EndArray(length)); if (count != N) { set_length_error(); return false; } this->parsed = true; return true; } bool reap_error(ErrorStack& stk) override { if (!the_error) return false; stk.push(the_error.release()); internal.reap_error(stk); return true; } bool write(IHandler* output) const override { if (!output->StartArray()) return false; for (auto&& e : *m_value) { Handler h(&e); if (!h.write(output)) return false; } return output->EndArray(static_cast(m_value->size())); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("array"), alloc); Value items; internal.generate_schema(items, alloc); output.AddMember(rapidjson::StringRef("items"), items, alloc); output.AddMember(rapidjson::StringRef("minItems"), static_cast(N), alloc); output.AddMember(rapidjson::StringRef("maxItems"), static_cast(N), alloc); } std::string type_name() const override { return "std::array<" + internal.type_name() + ", " + std::to_string(N) + ">"; } }; template class PointerHandler : public BaseHandler { public: typedef typename std::pointer_traits::element_type ElementType; protected: mutable PointerType* m_value; mutable std::unique_ptr> internal_handler; int depth = 0; protected: explicit PointerHandler(PointerType* value) : m_value(value) {} void initialize() { if (!internal_handler) { m_value->reset(new ElementType()); internal_handler.reset(new Handler(m_value->get())); } } void reset() override { depth = 0; internal_handler.reset(); m_value->reset(); } bool postcheck(bool success) { if (success) this->parsed = internal_handler->is_parsed(); return success; } public: bool Null() override { if (depth == 0) { m_value->reset(); this->parsed = true; return true; } else { initialize(); return postcheck(internal_handler->Null()); } } bool write(IHandler* out) const override { if (!m_value || !m_value->get()) { return out->Null(); } if (!internal_handler) { internal_handler.reset(new Handler(m_value->get())); } return internal_handler->write(out); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { const_cast*>(this)->initialize(); output.SetObject(); Value anyOf(rapidjson::kArrayType); Value nullDescriptor(rapidjson::kObjectType); nullDescriptor.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("null"), alloc); Value descriptor; internal_handler->generate_schema(descriptor, alloc); anyOf.PushBack(nullDescriptor, alloc); anyOf.PushBack(descriptor, alloc); output.AddMember(rapidjson::StringRef("anyOf"), anyOf, alloc); } bool Bool(bool b) override { initialize(); return postcheck(internal_handler->Bool(b)); } bool Int(int i) override { initialize(); return postcheck(internal_handler->Int(i)); } bool Uint(unsigned i) override { initialize(); return postcheck(internal_handler->Uint(i)); } bool Int64(std::int64_t i) override { initialize(); return postcheck(internal_handler->Int64(i)); } bool Uint64(std::uint64_t i) override { initialize(); return postcheck(internal_handler->Uint64(i)); } bool Double(double i) override { initialize(); return postcheck(internal_handler->Double(i)); } bool String(const char* str, SizeType len, bool copy) override { initialize(); return postcheck(internal_handler->String(str, len, copy)); } bool Key(const char* str, SizeType len, bool copy) override { initialize(); return postcheck(internal_handler->Key(str, len, copy)); } bool StartObject() override { initialize(); ++depth; return internal_handler->StartObject(); } bool EndObject(SizeType len) override { initialize(); --depth; return postcheck(internal_handler->EndObject(len)); } bool StartArray() override { initialize(); ++depth; return postcheck(internal_handler->StartArray()); } bool EndArray(SizeType len) override { initialize(); --depth; return postcheck(internal_handler->EndArray(len)); } bool has_error() const override { return internal_handler && internal_handler->has_error(); } bool reap_error(ErrorStack& stk) override { return internal_handler && internal_handler->reap_error(stk); } }; template class Handler> : public PointerHandler> { public: explicit Handler(std::unique_ptr* value) : PointerHandler>(value) { } std::string type_name() const override { if (this->internal_handler) { return "std::unique_ptr<" + this->internal_handler->type_name() + ">"; } return "std::unique_ptr"; } }; template class Handler> : public PointerHandler> { public: explicit Handler(std::shared_ptr* value) : PointerHandler>(value) {} std::string type_name() const override { if (this->internal_handler) { return "std::shared_ptr<" + this->internal_handler->type_name() + ">"; } return "std::shared_ptr"; } }; template class MapHandler : public BaseHandler { protected: typedef typename MapType::mapped_type ElementType; protected: ElementType element; Handler internal_handler; MapType* m_value; std::string current_key; int depth = 0; protected: void reset() override { element = ElementType(); current_key.clear(); internal_handler.prepare_for_reuse(); depth = 0; } bool precheck(const char* type) { if (depth <= 0) { set_type_mismatch(type); return false; } return true; } bool postcheck(bool success) { if (!success) { the_error.reset(new error::ObjectMemberError(current_key)); } else { if (internal_handler.is_parsed()) { m_value->emplace(std::move(current_key), std::move(element)); element = ElementType(); internal_handler.prepare_for_reuse(); } } return success; } public: explicit MapHandler(MapType* value) : element(), internal_handler(&element), m_value(value) {} bool Null() override { return precheck("null") && postcheck(internal_handler.Null()); } bool Bool(bool b) override { return precheck("bool") && postcheck(internal_handler.Bool(b)); } bool Int(int i) override { return precheck("int") && postcheck(internal_handler.Int(i)); } bool Uint(unsigned i) override { return precheck("unsigned") && postcheck(internal_handler.Uint(i)); } bool Int64(std::int64_t i) override { return precheck("int64_t") && postcheck(internal_handler.Int64(i)); } bool Uint64(std::uint64_t i) override { return precheck("uint64_t") && postcheck(internal_handler.Uint64(i)); } bool Double(double d) override { return precheck("double") && postcheck(internal_handler.Double(d)); } bool String(const char* str, SizeType length, bool copy) override { return precheck("string") && postcheck(internal_handler.String(str, length, copy)); } bool Key(const char* str, SizeType length, bool copy) override { if (depth > 1) return postcheck(internal_handler.Key(str, length, copy)); current_key.assign(str, length); return true; } bool StartArray() override { return precheck("array") && postcheck(internal_handler.StartArray()); } bool EndArray(SizeType length) override { return precheck("array") && postcheck(internal_handler.EndArray(length)); } bool StartObject() override { ++depth; if (depth > 1) return postcheck(internal_handler.StartObject()); else m_value->clear(); return true; } bool EndObject(SizeType length) override { --depth; if (depth > 0) return postcheck(internal_handler.EndObject(length)); this->parsed = true; return true; } bool reap_error(ErrorStack& errs) override { if (!this->the_error) return false; errs.push(this->the_error.release()); internal_handler.reap_error(errs); return true; } bool write(IHandler* out) const override { if (!out->StartObject()) return false; for (auto&& pair : *m_value) { if (!out->Key(pair.first.data(), static_cast(pair.first.size()), true)) return false; Handler h(&pair.second); if (!h.write(out)) return false; } return out->EndObject(static_cast(m_value->size())); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { Value internal_schema; internal_handler.generate_schema(internal_schema, alloc); output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("object"), alloc); Value empty_obj(rapidjson::kObjectType); output.AddMember(rapidjson::StringRef("properties"), empty_obj, alloc); output.AddMember(rapidjson::StringRef("additionalProperties"), internal_schema, alloc); } }; template class Handler> : public MapHandler> { public: explicit Handler(std::unordered_map* value) : MapHandler>(value) { } std::string type_name() const override { return "std::unordered_mapinternal_handler.type_name() + ">"; } }; template class Handler> : public MapHandler> { public: explicit Handler(std::map* value) : MapHandler>(value) { } std::string type_name() const override { return "std::mapinternal_handler.type_name() + ">"; } }; template class Handler> : public MapHandler> { public: explicit Handler(std::unordered_multimap* value) : MapHandler>(value) { } std::string type_name() const override { return "std::unordered_mulitimapinternal_handler.type_name() + ">"; } }; template class Handler> : public MapHandler> { public: explicit Handler(std::multimap* value) : MapHandler>(value) { } std::string type_name() const override { return "std::multimapinternal_handler.type_name() + ">"; } }; template class TupleHander : public BaseHandler { protected: std::array, N> handlers; std::size_t index = 0; int depth = 0; bool postcheck(bool success) { if (!success) { the_error.reset(new error::ArrayElementError(index)); return false; } if (handlers[index]->is_parsed()) { ++index; } return true; } protected: void reset() override { index = 0; depth = 0; for (auto&& h : handlers) h->prepare_for_reuse(); } public: bool Null() override { if (index >= N) return true; return postcheck(handlers[index]->Null()); } bool Bool(bool b) override { if (index >= N) return true; return postcheck(handlers[index]->Bool(b)); } bool Int(int i) override { if (index >= N) return true; return postcheck(handlers[index]->Int(i)); } bool Uint(unsigned i) override { if (index >= N) return true; return postcheck(handlers[index]->Uint(i)); } bool Int64(std::int64_t i) override { if (index >= N) return true; return postcheck(handlers[index]->Int64(i)); } bool Uint64(std::uint64_t i) override { if (index >= N) return true; return postcheck(handlers[index]->Uint64(i)); } bool Double(double d) override { if (index >= N) return true; return postcheck(handlers[index]->Double(d)); } bool String(const char* str, SizeType length, bool copy) override { if (index >= N) return true; return postcheck(handlers[index]->String(str, length, copy)); } bool Key(const char* str, SizeType length, bool copy) override { if (index >= N) return true; return postcheck(handlers[index]->Key(str, length, copy)); } bool StartArray() override { if (++depth > 1) { if (index >= N) return true; return postcheck(handlers[index]->StartArray()); } return true; } bool EndArray(SizeType length) override { if (--depth > 0) { if (index >= N) return true; return postcheck(handlers[index]->EndArray(length)); } this->parsed = true; return true; } bool StartObject() override { if (index >= N) return true; return postcheck(handlers[index]->StartObject()); } bool EndObject(SizeType length) override { if (index >= N) return true; return postcheck(handlers[index]->EndObject(length)); } bool reap_error(ErrorStack& errs) override { if (!this->the_error) return false; errs.push(this->the_error.release()); for (auto&& h : handlers) h->reap_error(errs); return true; } bool write(IHandler* out) const override { if (!out->StartArray()) return false; for (auto&& h : handlers) { if (!h->write(out)) return false; } return out->EndArray(N); } void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("array"), alloc); Value items(rapidjson::kArrayType); for (auto&& h : handlers) { Value item; h->generate_schema(item, alloc); items.PushBack(item, alloc); } output.AddMember(rapidjson::StringRef("items"), items, alloc); } }; namespace nonpublic { template struct TupleIniter { void operator()(std::unique_ptr* handlers, Tuple& t) const { handlers[index].reset( new Handler::type>(&std::get(t))); TupleIniter{}(handlers, t); } }; template struct TupleIniter { void operator()(std::unique_ptr* handlers, Tuple& t) const { (void)handlers; (void)t; } }; } template class Handler> : public TupleHander>::value> { private: static const std::size_t N = std::tuple_size>::value; public: explicit Handler(std::tuple* t) { nonpublic::TupleIniter<0, N, std::tuple> initer; initer(this->handlers.data(), *t); } std::string type_name() const override { std::string str = "std::tuple<"; for (auto&& h : this->handlers) { str += h->type_name(); str += ", "; } str.pop_back(); str.pop_back(); str += '>'; return str; } }; } ================================================ FILE: src/staticjson.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace staticjson { // Adapted from Jettison's implementation (http://jettison.codehaus.org/) // Original copyright (compatible with MIT): // Copyright 2006 Envoi Solutions LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. inline std::string quote(const std::string& str) { std::string sb; sb.reserve(str.size() + 8); sb += '\"'; typedef std::string::const_iterator iterator; for (iterator it = str.begin(), end = str.end(); it != end; ++it) { char c = *it; switch (c) { case '\\': case '"': sb += '\\'; sb += c; break; case '\b': sb.append("\\b", 2); break; case '\t': sb.append("\\t", 2); break; case '\n': sb.append("\\n", 2); break; case '\f': sb.append("\\f", 2); break; case '\r': sb.append("\\r", 2); break; case '\x00': sb.append("\\x00", 4); break; case '\x01': sb.append("\\x01", 4); break; case '\x02': sb.append("\\x02", 4); break; case '\x03': sb.append("\\x03", 4); break; case '\x04': sb.append("\\x04", 4); break; case '\x05': sb.append("\\x05", 4); break; case '\x06': sb.append("\\x06", 4); break; case '\x07': sb.append("\\x07", 4); break; case '\x0b': sb.append("\\x0b", 4); break; case '\x0e': sb.append("\\x0e", 4); break; case '\x0f': sb.append("\\x0f", 4); break; case '\x10': sb.append("\\x10", 4); break; case '\x11': sb.append("\\x11", 4); break; case '\x12': sb.append("\\x12", 4); break; case '\x13': sb.append("\\x13", 4); break; case '\x14': sb.append("\\x14", 4); break; case '\x15': sb.append("\\x15", 4); break; case '\x16': sb.append("\\x16", 4); break; case '\x17': sb.append("\\x17", 4); break; case '\x18': sb.append("\\x18", 4); break; case '\x19': sb.append("\\x19", 4); break; case '\x1a': sb.append("\\x1a", 4); break; case '\x1b': sb.append("\\x1b", 4); break; case '\x1c': sb.append("\\x1c", 4); break; case '\x1d': sb.append("\\x1d", 4); break; case '\x1e': sb.append("\\x1e", 4); break; case '\x1f': sb.append("\\x1f", 4); break; default: sb += c; } } sb += '\"'; return sb; } static std::string stringprintf(const char* format, ...) #ifndef _MSC_VER __attribute__((format(printf, 1, 2))) #endif ; static std::string stringprintf(const char* format, ...) { va_list ap; va_start(ap, format); int sz = vsnprintf(nullptr, 0, format, ap); va_end(ap); if (sz <= 0) return std::string(); std::string result(sz, 0); va_start(ap, format); vsnprintf(&result[0], static_cast(sz + 1), format, ap); va_end(ap); return result; } std::string error::Success::description() const { return "No error"; } std::string error::ObjectMemberError::description() const { return "Error at object member with name " + quote(member_name()); } std::string error::ArrayElementError::description() const { return "Error at array element at index " + std::to_string(index()); } std::string error::RequiredFieldMissingError::description() const { std::string result = "Missing required field(s): "; bool first = true; for (auto&& name : missing_members()) { if (!first) { result += ", "; } first = false; result += quote(name); } return result; } std::string error::NumberOutOfRangeError::description() const { return "Number out of range: expected type " + quote(expected_type()) + " but the type needed is " + quote(actual_type()); } std::string error::TypeMismatchError::description() const { return "Type mismatch between expected type " + quote(expected_type()) + " and actual type " + quote(actual_type()); } std::string error::DuplicateKeyError::description() const { return "Duplicate key in uniquely keyed map type: " + quote(key()); } std::string error::UnknownFieldError::description() const { return "Unknown field with name: " + quote(field_name()); } std::string error::RecursionTooDeepError::description() const { return "Too many levels of recursion"; } std::string error::TooManyLeavesError::description() const { return "Too many leaves"; } std::string error::CorruptedDOMError::description() const { return "JSON has invalid structure"; } std::string error::ArrayLengthMismatchError::description() const { return "The JSON array has different length than the required type"; } std::string error::InvalidEnumError::description() const { return quote(m_name) + " is an invalid enum name"; } std::string error::CustomError::description() const { return m_message; } std::string ParseStatus::short_description() const { if (!has_error()) { return std::string(); } return stringprintf( "Parsing failed at offset %lld with error code %d:\n%s\n", static_cast(m_offset), m_code, rapidjson::GetParseError_En(static_cast(m_code))); } std::string ParseStatus::description() const { std::string res = short_description(); if (m_stack) { res += "\nTraceback (last call first)\n"; for (auto&& err : m_stack) { res += "* "; res += err.description(); res += '\n'; } } return res; } IHandler::~IHandler() {} BaseHandler::~BaseHandler() {} bool BaseHandler::set_out_of_range(const char* actual_type) { the_error.reset(new error::NumberOutOfRangeError(type_name(), actual_type)); return false; } bool BaseHandler::set_type_mismatch(const char* actual_type) { the_error.reset(new error::TypeMismatchError(type_name(), actual_type)); return false; } bool IHandler::RawNumber(const char*, SizeType, bool) { fprintf(stderr, "%s", "Calling non-overridden IHandler::RawNumber() is a programming bug!\n"); std::terminate(); } ObjectHandler::ObjectHandler() : memory_pool_allocator(GlobalConfig::getInstance()->getMemoryChunkSize(), &mempool::get_crt_allocator()) , internals(decltype(internals)::allocator_type(&memory_pool_allocator)) , current_name(decltype(current_name)::allocator_type(&memory_pool_allocator)) , leavesStack(decltype(leavesStack)::container_type::allocator_type(&memory_pool_allocator)) { } ObjectHandler::~ObjectHandler() {} std::string ObjectHandler::type_name() const { return "object"; } void ObjectHandler::postinit() { size_t max_string_size = 0; for (const auto& pair : internals) { max_string_size = std::max(max_string_size, pair.first.size()); } if (max_string_size) { current_name.reserve(max_string_size); } } bool ObjectHandler::precheck(const char* actual_type) { if (depth <= 0) { the_error.reset(new error::TypeMismatchError(type_name(), actual_type)); return false; } if (current && current->handler && current->handler->is_parsed()) { if (flags & Flags::AllowDuplicateKey) { current->handler->prepare_for_reuse(); } else { the_error.reset(new error::DuplicateKeyError(mempool::to_std_string(current_name))); return false; } } return true; } bool ObjectHandler::postcheck(bool success) { if (!success) { the_error.reset(new error::ObjectMemberError(mempool::to_std_string(current_name))); } return success; } void ObjectHandler::set_missing_required(const std::string& name) { if (!the_error || the_error->type() != error::MISSING_REQUIRED) the_error.reset(new error::RequiredFieldMissingError()); std::vector& missing = static_cast(the_error.get())->missing_members(); missing.push_back(name); } #define POSTCHECK(x) (!current || !(current->handler) || postcheck(x)) bool ObjectHandler::Double(double value) { if (!precheck("double")) return false; return POSTCHECK(current->handler->Double(value)); } bool ObjectHandler::Int(int value) { if (!precheck("int")) return false; return POSTCHECK(current->handler->Int(value)); } bool ObjectHandler::Uint(unsigned value) { if (!precheck("unsigned")) return false; return POSTCHECK(current->handler->Uint(value)); } bool ObjectHandler::Bool(bool value) { if (!precheck("bool")) return false; return POSTCHECK(current->handler->Bool(value)); } bool ObjectHandler::Int64(std::int64_t value) { if (!precheck("std::int64_t")) return false; return POSTCHECK(current->handler->Int64(value)); } bool ObjectHandler::Uint64(std::uint64_t value) { if (!precheck("std::uint64_t")) return false; return POSTCHECK(current->handler->Uint64(value)); } bool ObjectHandler::Null() { if (!precheck("null")) return false; return POSTCHECK(current->handler->Null()); } bool ObjectHandler::StartCheckMaxDepthMaxLeaves(bool isArray) { if (GlobalConfig::getInstance()->isMaxDepthSet()) { ++jsonDepth; // If a leaf IE is an array of a simple data type, then the whole array shall be considered // as the first level of nesting. If a leaf IE is a data structure or an array of data // structures, then it shall be considered a branch and the first level of nesting. if (jsonDepth > GlobalConfig::getInstance()->getMaxDepth()) { if (!the_error || the_error->type() != error::TOO_DEEP_RECURSION) the_error.reset(new error::RecursionTooDeepError()); jsonDepth = 0; return false; } } if (GlobalConfig::getInstance()->isMaxLeavesSet()) { if (isArray) { lastLeafStat = true; } else { lastLeafStat = false; } leavesStack.push(0); } return true; } bool ObjectHandler::EndCheckMaxDepthMaxLeaves(SizeType sz, bool isArray) { if (GlobalConfig::getInstance()->isMaxDepthSet()) { --jsonDepth; } if (GlobalConfig::getInstance()->isMaxLeavesSet()) { if (isArray) { if (lastLeafStat && sz > 0) { // simple array type // sz > 0 check if it is an empty array, and should not increase total leaf number // for empty array // TS29501 chapter 6.2 // If a leaf IE is an array of a simple data type, then the whole array shall count // as one leaf. totalLeaves += 1; } } else { // object type totalLeaves += sz; totalLeaves -= leavesStack.top(); } if (totalLeaves > GlobalConfig::getInstance()->getMaxLeaves()) { if (!the_error || the_error->type() != error::TOO_MANY_LEAVES) the_error.reset(new error::TooManyLeavesError()); totalLeaves = 0; return false; } leavesStack.pop(); if (!leavesStack.empty()) { leavesStack.top()++; } lastLeafStat = false; } return true; } bool ObjectHandler::StartArray() { if (!StartCheckMaxDepthMaxLeaves(true)) return false; if (!precheck("array")) return false; return POSTCHECK(current->handler->StartArray()); } bool ObjectHandler::EndArray(SizeType sz) { if (!EndCheckMaxDepthMaxLeaves(sz, true)) return false; if (!precheck("array")) return false; return POSTCHECK(current->handler->EndArray(sz)); } bool ObjectHandler::String(const char* str, SizeType sz, bool copy) { if (!precheck("string")) return false; return POSTCHECK(current->handler->String(str, sz, copy)); } bool ObjectHandler::Key(const char* str, SizeType sz, bool copy) { if (depth <= 0) { the_error.reset(new error::CorruptedDOMError()); return false; } if (depth == 1) { current_name.assign(str, sz); auto it = internals.find(current_name); if (it == internals.end()) { current = nullptr; if ((flags & Flags::DisallowUnknownKey)) { the_error.reset(new error::UnknownFieldError(str, sz)); return false; } } else if (it->second.flags & Flags::IgnoreRead) { current = nullptr; } else { current = &it->second; } return true; } else { return POSTCHECK(current->handler->Key(str, sz, copy)); } } bool ObjectHandler::StartObject() { ++depth; if (!StartCheckMaxDepthMaxLeaves(false)) { return false; } if (depth > 1) { return POSTCHECK(current->handler->StartObject()); } return true; } bool ObjectHandler::EndObject(SizeType sz) { --depth; if (!EndCheckMaxDepthMaxLeaves(sz, false)) { return false; } if (depth > 0) { return POSTCHECK(current->handler->EndObject(sz)); } for (auto&& pair : internals) { if (pair.second.handler && !(pair.second.flags & Flags::Optional) && !pair.second.handler->is_parsed()) { set_missing_required(mempool::to_std_string(pair.first)); } } if (!the_error) { this->parsed = true; return true; } return false; } void ObjectHandler::reset() { current = nullptr; current_name.clear(); depth = 0; for (auto&& pair : internals) { if (pair.second.handler) pair.second.handler->prepare_for_reuse(); } } void ObjectHandler::add_handler(mempool::String&& name, ObjectHandler::FlaggedHandler&& fh) { internals.emplace(std::move(name), std::move(fh)); } bool ObjectHandler::reap_error(ErrorStack& stack) { if (!the_error) return false; stack.push(the_error.release()); if (current && current->handler) current->handler->reap_error(stack); return true; } bool ObjectHandler::write(IHandler* output) const { SizeType count = 0; if (!output->StartObject()) return false; for (auto&& pair : internals) { if (!pair.second.handler || (pair.second.flags & Flags::IgnoreWrite)) continue; if (!output->Key( pair.first.data(), static_cast(pair.first.size()), true)) return false; if (!pair.second.handler->write(output)) return false; ++count; } return output->EndObject(count); } void ObjectHandler::generate_schema(Value& output, MemoryPoolAllocator& alloc) const { output.SetObject(); output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("object"), alloc); Value properties(rapidjson::kObjectType); Value required(rapidjson::kArrayType); for (auto&& pair : internals) { Value schema; if (pair.second.handler) pair.second.handler->generate_schema(schema, alloc); else std::abort(); Value key; key.SetString(pair.first.c_str(), static_cast(pair.first.size()), alloc); properties.AddMember(key, schema, alloc); if (!(pair.second.flags & Flags::Optional)) { key.SetString(pair.first.c_str(), static_cast(pair.first.size()), alloc); required.PushBack(key, alloc); } } output.AddMember(rapidjson::StringRef("properties"), properties, alloc); if (!required.Empty()) { output.AddMember(rapidjson::StringRef("required"), required, alloc); } output.AddMember(rapidjson::StringRef("additionalProperties"), !(get_flags() & Flags::DisallowUnknownKey), alloc); } namespace nonpublic { template class IHandlerAdapter : public IHandler { private: T* t; public: explicit IHandlerAdapter(T* t) : t(t) {} virtual bool Null() override { return t->Null(); } virtual bool Bool(bool v) override { return t->Bool(v); } virtual bool Int(int v) override { return t->Int(v); } virtual bool Uint(unsigned v) override { return t->Uint(v); } virtual bool Int64(std::int64_t v) override { return t->Int64(v); } virtual bool Uint64(std::uint64_t v) override { return t->Uint64(v); } virtual bool Double(double v) override { return t->Double(v); } virtual bool String(const char* str, SizeType sz, bool copy) override { return t->String(str, sz, copy); } virtual bool StartObject() override { return t->StartObject(); } virtual bool Key(const char* str, SizeType sz, bool copy) override { return t->Key(str, sz, copy); } virtual bool EndObject(SizeType sz) override { return t->EndObject(sz); } virtual bool StartArray() override { return t->StartArray(); } virtual bool EndArray(SizeType sz) override { return t->EndArray(sz); } virtual void prepare_for_reuse() override { std::terminate(); } }; template static bool read_json(InputStream& is, BaseHandler* h, ParseStatus* status) { rapidjson::Reader r; rapidjson::ParseResult rc = r.Parse(is, *h); if (status) { status->set_result(rc.Code(), rc.Offset()); h->reap_error(status->error_stack()); } return rc.Code() == 0; } bool parse_json_string(const char* str, BaseHandler* handler, ParseStatus* status) { rapidjson::StringStream is(str); return read_json(is, handler, status); } bool parse_json_file(std::FILE* fp, BaseHandler* handler, ParseStatus* status) { if (!fp) return false; char buffer[1000]; rapidjson::FileReadStream is(fp, buffer, sizeof(buffer)); return read_json(is, handler, status); } struct StringOutputStream : private NonMobile { typedef char Ch; std::string* str; void Put(char c) { str->push_back(c); } void Flush() {} }; std::string serialize_json_string(const BaseHandler* handler) { std::string result; StringOutputStream os; os.str = &result; rapidjson::Writer writer(os); IHandlerAdapter adapter(&writer); handler->write(&adapter); return result; } bool serialize_json_file(std::FILE* fp, const BaseHandler* handler) { if (!fp) return false; char buffer[1000]; rapidjson::FileWriteStream os(fp, buffer, sizeof(buffer)); rapidjson::Writer writer(os); IHandlerAdapter adapter(&writer); return handler->write(&adapter); } std::string serialize_pretty_json_string(const BaseHandler* handler) { std::string result; StringOutputStream os; os.str = &result; rapidjson::PrettyWriter writer(os); IHandlerAdapter adapter(&writer); handler->write(&adapter); result.push_back('\n'); return result; } bool serialize_pretty_json_file(std::FILE* fp, const BaseHandler* handler) { if (!fp) return false; char buffer[1000]; rapidjson::FileWriteStream os(fp, buffer, sizeof(buffer)); rapidjson::PrettyWriter writer(os); IHandlerAdapter adapter(&writer); bool res = handler->write(&adapter); if (res) { putc('\n', fp); } return res; } bool write_value(const Value& v, BaseHandler* out, ParseStatus* status) { if (!v.Accept(*static_cast(out))) { if (status) { status->set_result(rapidjson::kParseErrorTermination, 0); out->reap_error(status->error_stack()); } return false; } return true; } bool read_value(Value* v, MemoryPoolAllocator* alloc, const BaseHandler* input, ParseStatus* status) { JSONHandler handler(v, alloc); if (!input->write(&handler)) { if (status) { status->set_result(rapidjson::kParseErrorTermination, 0); handler.reap_error(status->error_stack()); } return false; } return true; } } JSONHandler::JSONHandler(Value* v, MemoryPoolAllocator* a) : m_stack(), m_value(v), m_alloc(a) { m_stack.reserve(25); } bool JSONHandler::set_corrupted_dom() { the_error.reset(new error::CorruptedDOMError()); return false; } std::string JSONHandler::type_name() const { return "JSON"; } bool JSONHandler::stack_push() { m_stack.emplace_back(); return true; } void JSONHandler::stack_pop() { if (!m_stack.empty()) m_stack.pop_back(); } Value& JSONHandler::stack_top() { if (m_stack.empty()) return *m_value; return m_stack.back(); } bool JSONHandler::postprocess() { if (m_stack.empty()) { this->parsed = true; return true; } Value top1(std::move(stack_top())); stack_pop(); if (stack_top().IsArray()) { stack_top().PushBack(top1, *m_alloc); return stack_push(); } else if (stack_top().IsString()) { Value key(std::move(stack_top())); stack_pop(); if (!stack_top().IsObject()) return set_corrupted_dom(); stack_top().AddMember(key, top1, *m_alloc); return true; } return set_corrupted_dom(); } bool JSONHandler::Null() { stack_top().SetNull(); return postprocess(); } bool JSONHandler::Bool(bool b) { stack_top().SetBool(b); return postprocess(); } bool JSONHandler::Double(double d) { stack_top().SetDouble(d); return postprocess(); } bool JSONHandler::Int(int i) { stack_top().SetInt(i); return postprocess(); } bool JSONHandler::Int64(std::int64_t i) { stack_top().SetInt64(i); return postprocess(); } bool JSONHandler::Uint(unsigned int i) { stack_top().SetUint(i); return postprocess(); } bool JSONHandler::Uint64(std::uint64_t i) { stack_top().SetUint64(i); return postprocess(); } bool JSONHandler::String(const char* str, SizeType sz, bool copy) { if (copy) stack_top().SetString(str, sz, *m_alloc); else stack_top().SetString(str, sz); return postprocess(); } bool JSONHandler::Key(const char* str, SizeType sz, bool copy) { if (!stack_top().IsObject()) return set_corrupted_dom(); if (!stack_push()) return false; if (copy) stack_top().SetString(str, sz, *m_alloc); else stack_top().SetString(str, sz); return stack_push(); } bool JSONHandler::StartArray() { stack_top().SetArray(); return stack_push(); } bool JSONHandler::EndArray(SizeType) { stack_pop(); if (!stack_top().IsArray()) return set_corrupted_dom(); return postprocess(); } bool JSONHandler::StartObject() { stack_top().SetObject(); return true; } bool JSONHandler::EndObject(SizeType) { if (!stack_top().IsObject()) return set_corrupted_dom(); return postprocess(); } void JSONHandler::reset(MemoryPoolAllocator* a) { for (Value& v : m_stack) { v.SetNull(); } m_stack.clear(); m_alloc = a; } void JSONHandler::reset() { // Not implemented. See https://github.com/netheril96/StaticJSON/issues/41. std::terminate(); } bool JSONHandler::write(IHandler* output) const { return m_value->Accept(*output); } GlobalConfig* GlobalConfig::getInstance() noexcept { static GlobalConfig config; return &config; } namespace mempool { [[noreturn]] void throw_bad_alloc() { #ifdef __cpp_exceptions throw std::bad_alloc(); #else abort(); #endif } rapidjson::CrtAllocator& get_crt_allocator() noexcept { static rapidjson::CrtAllocator a; return a; } } } ================================================ FILE: test/myarray.hpp ================================================ #include #include // This class is used to test custom conversion functions in StaticJSON. template class Array { private: T* m_data; size_t m_size; public: explicit Array() : m_data(nullptr), m_size(0) {} explicit Array(size_t size) : m_size(size) { m_data = new T[size]; } ~Array() { clear(); } Array(Array&& that) noexcept { m_data = that.m_data; m_size = that.m_size; that.m_data = nullptr; that.m_size = 0; } Array& operator=(Array&& that) noexcept { std::swap(m_data, that.m_data); std::swap(m_size, that.m_size); return *this; } const T& operator[](size_t i) const { return m_data[i]; } T& operator[](size_t i) { return m_data[i]; } size_t size() const { return m_size; } const T& back() const { return m_data[m_size - 1]; } T& back() { return m_data[m_size - 1]; } const T& front() const { return m_data[0]; } T& front() { return m_data[0]; } bool empty() const { return m_size == 0; } void clear() { delete[] m_data; m_data = nullptr; m_size = 0; } }; namespace staticjson { template struct Converter> { typedef std::deque shadow_type; static std::unique_ptr from_shadow(const shadow_type& shadow, Array& value) { value = Array(shadow.size()); for (size_t i = 0; i < shadow.size(); ++i) { value[i] = shadow[i]; } return nullptr; } static void to_shadow(const Array& value, shadow_type& shadow) { shadow.resize(value.size()); for (size_t i = 0; i < shadow.size(); ++i) { shadow[i] = value[i]; } } }; } ================================================ FILE: test/test_autojsoncxx.cpp ================================================ // The MIT License (MIT) // // Copyright (c) 2014 Siyuan Ren (netheril96@gmail.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include "catch.hpp" #define AUTOJSONCXX_ROOT_DIRECTORY get_base_dir() + "/autojsoncxx/" + #include #include "userdef.hpp" #include #include using namespace autojsoncxx; using namespace config; using namespace config::event; const std::string& get_base_dir(void); namespace config { inline bool operator==(Date d1, Date d2) { return d1.year == d2.year && d1.month == d2.month && d1.day == d2.day; } inline bool operator!=(Date d1, Date d2) { return !(d1 == d2); } } namespace config { namespace event { bool operator==(const BlockEvent& b1, const BlockEvent& b2) { return b1.admin_ID == b2.admin_ID && b1.date == b2.date && b1.description == b2.description && b1.details == b2.details && b1.serial_number == b2.serial_number; } } bool operator==(const User& u1, const User& u2) { return u1.birthday == u2.birthday && (u1.block_event == u2.block_event || (u1.block_event && u2.block_event && *u1.block_event == *u2.block_event)) && u1.dark_history == u2.dark_history && u1.ID == u2.ID && u1.nickname == u2.nickname && u1.optional_attributes == u2.optional_attributes; } } static std::string read_all(const std::string& file_name) { std::ifstream input(file_name.c_str()); std::ostringstream buffer; buffer << input.rdbuf(); return buffer.str(); } static Date create_date(int year, int month, int day) { Date d; d.year = year; d.month = month; d.day = day; return d; } // If most of the cases fail, you probably set the work directory wrong. // Point the work directory to the `test/` subdirectory // or redefine the macro AUTOJSONCXX_ROOT_DIRECTORY. TEST_CASE("Test for the constructor of generated class (old)", "[code generator]") { User user; REQUIRE(user.ID == 0ULL); // Do not use string literal because MSVC will mess up the encoding static const char default_nickname[] = {char(0xe2), char(0x9d), char(0xb6), char(0xe2), char(0x9d), char(0xb7), char(0xe2), char(0x9d), char(0xb8)}; REQUIRE(user.nickname.size() == sizeof(default_nickname)); REQUIRE(std::equal(user.nickname.begin(), user.nickname.end(), default_nickname)); REQUIRE(user.birthday == create_date(0, 0, 0)); REQUIRE(user.dark_history.empty()); REQUIRE(user.optional_attributes.empty()); REQUIRE(!user.block_event); BlockEvent event; REQUIRE(event.admin_ID == 255ULL); REQUIRE(event.date == create_date(1970, 1, 1)); REQUIRE(event.serial_number == 0ULL); REQUIRE(event.details.empty()); } TEST_CASE("Test for correct parsing (old)", "[parsing]") { SECTION("Test for an array of user", "[parsing]") { std::vector users; ParsingResult err; bool success = from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array.json", users, err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users.size() == 2); { const User& u = users.front(); REQUIRE(u.ID == 7947402710862746952ULL); REQUIRE(u.nickname == "bigger than bigger"); REQUIRE(u.birthday == create_date(1984, 9, 2)); REQUIRE(u.block_event.get() != nullptr); const BlockEvent& e = *u.block_event; REQUIRE(e.admin_ID > 0ULL); REQUIRE(e.date == create_date(1970, 12, 31)); REQUIRE(e.description == "advertisement"); REQUIRE(e.details.size() > 0ULL); REQUIRE(u.dark_history.empty()); REQUIRE(u.optional_attributes.empty()); } { const User& u = users.back(); REQUIRE(u.ID == 13478355757133566847ULL); REQUIRE(u.nickname.size() == 15); REQUIRE(!u.block_event); REQUIRE(u.optional_attributes.size() == 3); REQUIRE(u.optional_attributes.find("Self description") != u.optional_attributes.end()); } } SECTION("Test for a map of user", "[parsing]") { std::unordered_map users; ParsingResult err; bool success = from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_map.json", users, err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users.size() == 2); { const User& u = users["First"]; REQUIRE(u.ID == 7947402710862746952ULL); REQUIRE(u.nickname == "bigger than bigger"); REQUIRE(u.birthday == create_date(1984, 9, 2)); REQUIRE(u.block_event.get() != nullptr); const BlockEvent& e = *u.block_event; REQUIRE(e.admin_ID > 0ULL); REQUIRE(e.date == create_date(1970, 12, 31)); REQUIRE(e.description == "advertisement"); REQUIRE(e.details.size() > 0ULL); REQUIRE(u.dark_history.empty()); REQUIRE(u.optional_attributes.empty()); } { const User& u = users["Second"]; REQUIRE(u.ID == 13478355757133566847ULL); REQUIRE(u.nickname.size() == 15); REQUIRE(!u.block_event); REQUIRE(u.optional_attributes.size() == 3); REQUIRE(u.optional_attributes.find("Self description") != u.optional_attributes.end()); } } } TEST_CASE("Test for mismatch between JSON and C++ class std::vector (old)", "[parsing], [error]") { std::vector users; ParsingResult err; SECTION("Mismatch between array and object", "[parsing], [error], [type mismatch]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/single_object.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); REQUIRE(std::distance(err.begin(), err.end()) == 1); auto&& e = static_cast(*err.begin()); // REQUIRE(e.expected_type() == "array"); REQUIRE(e.actual_type() == "object"); } SECTION("Required field not present; test the path as well", "[parsing], [error], [missing required]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/missing_required.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(std::distance(err.begin(), err.end()) == 5); auto it = err.begin(); REQUIRE(it->type() == error::MISSING_REQUIRED); ++it; REQUIRE(it->type() == error::OBJECT_MEMBER); REQUIRE(static_cast(*it).member_name() == "date"); ++it; REQUIRE(it->type() == error::ARRAY_ELEMENT); REQUIRE(static_cast(*it).index() == 0); ++it; REQUIRE(it->type() == error::OBJECT_MEMBER); REQUIRE(static_cast(*it).member_name() == "dark_history"); } SECTION("Unknown field in strict parsed class Date", "[parsing], [error], [unknown field]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/unknown_field.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); REQUIRE(static_cast(*err.begin()).field_name() == "hour"); } SECTION("Duplicate key in class User", "[parsing], [error], [duplicate key]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/duplicate_key_user.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::DUPLICATE_KEYS); REQUIRE(static_cast(*err.begin()).key() == "ID"); } SECTION("Out of range", "[parsing], [error], [out of range]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/out_of_range.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::NUMBER_OUT_OF_RANGE); } SECTION("Mismatch between integer and string", "[parsing], [error], [type mismatch]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/integer_string.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); } SECTION("Null character in key", "[parsing], [error], [null character]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/null_in_key.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); } } TEST_CASE("Test for mismatch between JSON and C++ class std::map (old)", "[parsing], [error]") { std::map users; ParsingResult err; SECTION("Mismatch between object and array", "[parsing], [error], [type mismatch]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); auto&& e = static_cast(*err.begin()); // REQUIRE(e.expected_type() == "object"); REQUIRE(e.actual_type() == "array"); } SECTION("Mismatch in mapped element", "[parsing], [error], [type mismatch]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/map_element_mismatch.json", users, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); { REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); auto&& e = static_cast(*err.begin()); (void)e; } { auto it = ++err.begin(); REQUIRE(it != err.end()); REQUIRE(it->type() == error::OBJECT_MEMBER); auto&& e = static_cast(*it); REQUIRE(e.member_name() == "Third"); } } } TEST_CASE("Test for writing JSON (old)", "[serialization]") { std::vector users; ParsingResult err; bool success = from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array.json", users, err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users.size() == 2); std::string str = to_json_string(users); std::vector copied_users; success = from_json_string(str, copied_users, err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users == copied_users); } TEST_CASE("Test for DOM support (old)", "[DOM]") { rapidjson::Document doc; ParsingResult err; bool success = from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array_compact.json", doc, err); { CAPTURE(err.description()); REQUIRE(success); } SECTION("Test for parsed result", "[DOM], [parsing]") { REQUIRE(doc.IsArray()); REQUIRE(doc.Size() == 2); const rapidjson::Value& second = doc[1u]; REQUIRE(second["ID"].IsUint64()); REQUIRE(second["ID"].GetUint64() == 13478355757133566847ULL); REQUIRE(second["block_event"].IsNull()); REQUIRE(second["dark_history"].IsArray()); REQUIRE(second["dark_history"][0u].IsObject()); REQUIRE(second["dark_history"][0u]["description"] == "copyright infringement"); } SECTION("Test for serialization", "[DOM], [serialization]") { std::string output; to_json_string(output, doc); REQUIRE( output == read_all(AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/user_array_compact.json")); } SECTION("Test for to/from DOM", "[DOM], [conversion]") { std::vector users; error::ErrorStack errs; REQUIRE(from_document(users, doc, errs)); REQUIRE(users.size() == 2); REQUIRE(users[0].birthday == create_date(1984, 9, 2)); REQUIRE(users[0].block_event); REQUIRE(users[0].block_event->details == "most likely a troll"); rapidjson::Document another_doc; to_document(users, another_doc); REQUIRE(doc == another_doc); } } TEST_CASE("Test for parsing tuple type (old)", "[parsing], [tuple]") { typedef std::tuple>, bool> hard_type; hard_type hetero_array; ParsingResult err; SECTION("Test for valid tuple", "[parsing], [tuple]") { bool success = from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/success/hard.json", hetero_array, err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(std::get<1>(hetero_array) == -65535); REQUIRE(std::get::value - 1>(hetero_array) == false); } SECTION("Test for invalid tuple", "[parsing], [tuple], [error]") { REQUIRE(!from_json_file( AUTOJSONCXX_ROOT_DIRECTORY "/examples/failure/hard.json", hetero_array, err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); REQUIRE(static_cast(*err.begin()).actual_type() == "null"); } } ================================================ FILE: test/test_basic.cpp ================================================ #include #include "catch.hpp" using namespace staticjson; struct MyObject { int i; void staticjson_init(ObjectHandler* h) { h->set_flags(Flags::DisallowUnknownKey); h->add_property("i", &i); } }; TEST_CASE("Basic test") { MyObject obj; const char* input = "{\"i\": -980008}"; REQUIRE(from_json_string(input, &obj, nullptr)); REQUIRE(obj.i == -980008); } TEST_CASE("Failure test") { MyObject obj; const char* input = ("{\"i\": -980008, \"j\": 42}"); ParseStatus res; REQUIRE(!from_json_string(input, &obj, &res)); CAPTURE(res.description()); REQUIRE(obj.i == -980008); } TEST_CASE("Vector test") { std::vector integers; const char* input = ("[1,2,3,4,5,6]"); ParseStatus res; bool success = from_json_string(input, &integers, nullptr); CAPTURE(res.description()); REQUIRE(success); REQUIRE(integers.size() == 6); } TEST_CASE("Serial") { REQUIRE(to_json_string(123) == "123"); MyObject obj; obj.i = 999; REQUIRE(to_pretty_json_string(obj).size() > 0); REQUIRE(to_json_string(std::vector{1, 2, 3, 4, 5, 6}) == "[1,2,3,4,5,6]"); } ================================================ FILE: test/test_example.cpp ================================================ #undef NDEBUG #include #include int builtin_test() { using namespace staticjson; std::string a = to_json_string(std::vector{1.0, 2.0, -3.1415}); std::string b = to_pretty_json_string(std::map>>{}); std::vector> data; const char* json_string = "[{\" hello \": 535353, \" world \": 849}, {\" k \": -548343}]"; assert(from_json_string(json_string, &data, nullptr)); assert(data.size() == 2); assert(data[1][" k "] == -548343); to_pretty_json_file("/tmp/B11FA212-ED7F-4577-83B1-CC7ADBBC8ED3.json", data); return 0; } #include "catch.hpp" TEST_CASE("Example test") { REQUIRE(builtin_test() == 0); } ================================================ FILE: test/test_instantiation.cpp ================================================ #include #include #define INSTANTIATE(type) \ { \ staticjson::Handler h(nullptr); \ } // Ensure that the template classes can be instantiated without compile time error void instantiate_all_types() { INSTANTIATE(char) INSTANTIATE(bool) INSTANTIATE(int) INSTANTIATE(unsigned) INSTANTIATE(std::int64_t) INSTANTIATE(std::uint64_t) INSTANTIATE(std::string) typedef std::array my_array; INSTANTIATE(my_array) INSTANTIATE(std::vector) INSTANTIATE(std::vector) INSTANTIATE(std::deque>>>>); INSTANTIATE(staticjson::Document); INSTANTIATE(decltype(nullptr)); typedef std::tuple> complex_tuple_type; INSTANTIATE(complex_tuple_type); } #undef INSTANTIATE ================================================ FILE: test/test_integration.cpp ================================================ #include "catch.hpp" #include "myarray.hpp" #include #include #include #include #include #include #include #include #include #include #ifdef __has_include #if __has_include() #pragma message("INFO: available for testing") #include #define STATICJSON_OPTIONAL 1 #endif #endif #ifdef _MSC_VER #define stat _stat #endif const std::string& get_base_dir() { struct Initializer { std::string path; Initializer() { path = "."; struct stat st; for (int i = 0; i < 16; ++i) { if (::stat((path + "/examples").c_str(), &st) == 0) { return; } else { path += "/.."; } } fprintf(stderr, "%s", "No 'examples' directory found in the working directory or its ancestors\n"); std::abort(); } }; static const Initializer init; return init.path; } using namespace staticjson; enum class CalendarType { Gregorian, Chinese, Jewish, Islam }; STATICJSON_DECLARE_ENUM(CalendarType, {"Gregorian", CalendarType::Gregorian}, {"Chinese", CalendarType::Chinese}, {"Jewish", CalendarType::Jewish}, {"Islam", CalendarType::Islam}) struct Date { int year, month, day; CalendarType type; Date() : year(0), month(0), day(0), type(CalendarType::Gregorian) {} }; namespace staticjson { void init(Date* d, ObjectHandler* h) { h->add_property("year", &d->year); h->add_property("month", &d->month); h->add_property("day", &d->day); h->add_property("type", &d->type, Flags::Optional); h->set_flags(Flags::DisallowUnknownKey); } } struct BlockEvent { std::uint64_t serial_number, admin_ID = 255; Date date; std::string description, details; #ifdef STATICJSON_OPTIONAL staticjson::optional flags; #endif void staticjson_init(ObjectHandler* h) { h->add_property("serial_number", &serial_number); h->add_property("administrator ID", &admin_ID, Flags::Optional); h->add_property("date", &date, Flags::Optional); h->add_property("description", &description, Flags::Optional); h->add_property("details", &details, Flags::Optional); #ifdef STATICJSON_OPTIONAL h->add_property("flags", &flags, Flags::Optional); #endif } }; struct User { friend class Handler; unsigned long long ID; std::string nickname; Date birthday; std::shared_ptr block_event; std::vector dark_history; std::unordered_map optional_attributes; std::tuple>, bool> auxiliary; #ifdef STATICJSON_OPTIONAL staticjson::optional dark_event; staticjson::optional>> alternate_history; #endif }; namespace staticjson { template <> class Handler : public ObjectHandler { public: explicit Handler(User* user) { auto h = this; h->add_property("ID", &user->ID); h->add_property("nickname", &user->nickname); h->add_property("birthday", &user->birthday, Flags::Optional); h->add_property("block_event", &user->block_event, Flags::Optional); h->add_property("optional_attributes", &user->optional_attributes, Flags::Optional); h->add_property("dark_history", &user->dark_history, Flags::Optional); h->add_property("auxiliary", &user->auxiliary, Flags::Optional); #ifdef STATICJSON_OPTIONAL h->add_property("dark_event", &user->dark_event, Flags::Optional); h->add_property("alternate_history", &user->alternate_history, Flags::Optional); #endif } std::string type_name() const override { return "User"; } }; } inline bool operator==(Date d1, Date d2) { return d1.year == d2.year && d1.month == d2.month && d1.day == d2.day; } inline bool operator!=(Date d1, Date d2) { return !(d1 == d2); } inline bool operator==(const BlockEvent& b1, const BlockEvent& b2) { return b1.admin_ID == b2.admin_ID && b1.date == b2.date && b1.description == b2.description && b1.details == b2.details; } inline bool operator!=(const BlockEvent& b1, const BlockEvent& b2) { return !(b1 == b2); } inline bool operator==(const User& u1, const User& u2) { return u1.birthday == u2.birthday && u1.ID == u2.ID && u1.nickname == u2.nickname && u1.dark_history == u2.dark_history && u1.optional_attributes == u2.optional_attributes && ((!u1.block_event && !u2.block_event) || ((u1.block_event && u2.block_event) && *u1.block_event == *u2.block_event)); } inline bool operator!=(const User& u1, const User& u2) { return !(u1 == u2); } inline std::string read_all(const std::string& file_name) { nonpublic::FileGuard fg(std::fopen(file_name.c_str(), "rb")); if (!fg.fp) throw std::system_error(errno, std::system_category()); std::string result; while (1) { int ch = getc(fg.fp); if (ch == EOF) break; result.push_back(static_cast(ch)); } return result; } inline Date create_date(int year, int month, int day) { Date d; d.year = year; d.month = month; d.day = day; return d; } void check_first_user(const User& u) { REQUIRE(u.ID == 7947402710862746952ULL); REQUIRE(u.nickname == "bigger than bigger"); REQUIRE(u.birthday == create_date(1984, 9, 2)); REQUIRE(u.block_event.get() != nullptr); const BlockEvent& e = *u.block_event; REQUIRE(e.admin_ID > 0ULL); REQUIRE(e.date == create_date(1970, 12, 31)); REQUIRE(e.description == "advertisement"); REQUIRE(e.details.size() > 0ULL); REQUIRE(u.dark_history.empty()); REQUIRE(u.optional_attributes.empty()); #ifdef STATICJSON_OPTIONAL REQUIRE(!!u.dark_event); REQUIRE(u.dark_event->serial_number == 9876543210123456789ULL); REQUIRE(u.dark_event->admin_ID == 11223344556677889900ULL); REQUIRE(u.dark_event->date == create_date(1970, 12, 31)); REQUIRE(u.dark_event->description == "promote"); REQUIRE(u.dark_event->details == "at least like a troll"); REQUIRE(static_cast(u.dark_event->flags) == false); REQUIRE(static_cast(u.alternate_history) == true); REQUIRE(u.alternate_history->size() == 2); REQUIRE(static_cast(u.alternate_history->at(0)) == false); REQUIRE(static_cast(u.alternate_history->at(1)) == true); { const auto& opt_e = u.alternate_history->at(1); REQUIRE(opt_e); const BlockEvent& e = *opt_e; REQUIRE(e.serial_number == 1123581321345589ULL); REQUIRE(e.admin_ID == 1123581321345589ULL); REQUIRE(e.date == create_date(1970, 12, 31)); REQUIRE(e.description == "redacted"); REQUIRE(e.details == "redacted"); REQUIRE(static_cast(e.flags) == true); REQUIRE(*e.flags == "x"); } #endif } void check_second_user(const User& u) { REQUIRE(u.ID == 13478355757133566847ULL); REQUIRE(u.nickname.size() == 15); REQUIRE(!u.block_event); REQUIRE(u.optional_attributes.size() == 3); REQUIRE(u.optional_attributes.find("Self description") != u.optional_attributes.end()); } template void check_array_of_user(const ArrayOfUsers& users) { REQUIRE(users.size() == 3); check_first_user(users[0]); check_second_user(users[1]); } void check_array_of_user(const Document& users) { REQUIRE(users.IsArray()); REQUIRE(users.Size() == 3); const Value& u = users[0]; REQUIRE(u.IsObject()); REQUIRE(u.HasMember("ID")); REQUIRE(u["ID"].IsUint64()); REQUIRE(u["ID"].GetUint64() == 7947402710862746952ULL); REQUIRE(u.HasMember("nickname")); REQUIRE(u["nickname"].IsString()); REQUIRE(std::strcmp(u["nickname"].GetString(), "bigger than bigger") == 0); REQUIRE(u.HasMember("birthday")); REQUIRE(u["birthday"].IsObject()); REQUIRE(u["birthday"].HasMember("year")); REQUIRE(u["birthday"]["year"] == 1984); REQUIRE(u.HasMember("block_event")); const Value& e = u["block_event"]; REQUIRE(e.HasMember("administrator ID")); REQUIRE(e.HasMember("description")); const Value& desc = e["description"]; REQUIRE(desc.IsString()); REQUIRE(std::strcmp(desc.GetString(), "advertisement") == 0); #ifdef STATICJSON_OPTIONAL REQUIRE(u.HasMember("dark_event")); { const Value& e = u["dark_event"]; REQUIRE(e.HasMember("administrator ID")); REQUIRE(e.HasMember("description")); REQUIRE(e["flags"].IsNull()); } REQUIRE(u.HasMember("alternate_history")); { const Value& e = u["alternate_history"]; REQUIRE(e.IsArray()); REQUIRE(e[0].IsNull()); REQUIRE(e[1].IsObject()); REQUIRE(e[1]["flags"].IsString()); } #endif } TEST_CASE("Test for correct parsing", "[parsing],[c]") { SECTION("Simple date parsing", "[parsing]") { Date d; ParseStatus err; bool success = from_json_string( "{\"year\": 1900, \"day\": 3, \"month\": 11, \"type\": \"Chinese\"}", &d, &err); { CAPTURE(err.description()); REQUIRE(success); } } SECTION("Test for an array of user", "[parsing]") { Array users; ParseStatus err; // Do it twice to test if vectors are reset properly at parsing. for (int i = 0; i < 2; ++i) { bool success = from_json_file( get_base_dir() + "/examples/success/user_array.json", &users, &err); { CAPTURE(err.description()); REQUIRE(success); } check_array_of_user(users); REQUIRE(users[0].birthday.type == CalendarType::Jewish); } Document d; REQUIRE(to_json_document(&d, users, nullptr)); check_array_of_user(d); } SECTION("Test for document", "[parsing]") { Document users; ParseStatus err; bool success = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); { CAPTURE(err.description()); REQUIRE(success); } check_array_of_user(users); std::vector vusers; REQUIRE(from_json_document(users, &vusers, nullptr)); check_array_of_user(vusers); } SECTION("Test for a map of user", "[parsing], [q]") { std::unordered_map users; ParseStatus err; // Do it twice to test if map is reset properly at parsing. for (int i = 0; i < 2; ++i) { bool success = from_json_file(get_base_dir() + "/examples/success/user_map.json", &users, &err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users.size() == 2); check_first_user(users["First"]); check_second_user(users["Second"]); } } } TEST_CASE("Test for mismatch between JSON and C++ class std::vector", "[parsing], [error]") { std::vector users; ParseStatus err; SECTION("Mismatch between array and object", "[parsing], [error], [type mismatch]") { REQUIRE( !from_json_file(get_base_dir() + "/examples/failure/single_object.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); REQUIRE(std::distance(err.begin(), err.end()) == 1); } SECTION("Missing requied simple", "[missing required]") { Date date; const char* str = "{ \"month\": 12 }"; REQUIRE(!from_json_string(str, &date, nullptr)); } SECTION("Required field not present; test the path as well", "[parsing], [error], [missing required]") { REQUIRE(!from_json_file( get_base_dir() + "/examples/failure/missing_required.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(std::distance(err.begin(), err.end()) == 5); auto it = err.begin(); REQUIRE(it->type() == error::MISSING_REQUIRED); ++it; REQUIRE(it->type() == error::OBJECT_MEMBER); REQUIRE(static_cast(*it).member_name() == "date"); ++it; REQUIRE(it->type() == error::ARRAY_ELEMENT); REQUIRE(static_cast(*it).index() == 0); ++it; REQUIRE(it->type() == error::OBJECT_MEMBER); REQUIRE(static_cast(*it).member_name() == "dark_history"); } SECTION("Unknown field in strict parsed class Date", "[parsing], [error], [unknown field]") { REQUIRE( !from_json_file(get_base_dir() + "/examples/failure/unknown_field.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); REQUIRE(static_cast(*err.begin()).field_name() == "hour"); } SECTION("Duplicate key in class User", "[parsing], [error], [duplicate key]") { REQUIRE(!from_json_file( get_base_dir() + "/examples/failure/duplicate_key_user.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::DUPLICATE_KEYS); REQUIRE(static_cast(*err.begin()).key() == "ID"); } SECTION("Out of range", "[parsing], [error], [out of range]") { REQUIRE( !from_json_file(get_base_dir() + "/examples/failure/out_of_range.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::NUMBER_OUT_OF_RANGE); } SECTION("Mismatch between integer and string", "[parsing], [error], [type mismatch]") { REQUIRE(!from_json_file( get_base_dir() + "/examples/failure/integer_string.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); } SECTION("Null character in key", "[parsing], [error], [null character]") { REQUIRE( !from_json_file(get_base_dir() + "/examples/failure/null_in_key.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::UNKNOWN_FIELD); } } TEST_CASE("Test for mismatch between JSON and C++ class std::map", "[parsing], [error]") { std::map users; ParseStatus err; SECTION("Mismatch between object and array", "[parsing], [error], [type mismatch]") { REQUIRE( !from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); auto&& e = static_cast(*err.begin()); REQUIRE(e.actual_type() == "array"); } SECTION("Mismatch in mapped element", "[parsing], [error], [type mismatch]") { REQUIRE(!from_json_file( get_base_dir() + "/examples/failure/map_element_mismatch.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); { REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); auto&& e = static_cast(*err.begin()); (void)e; } { auto it = ++err.begin(); REQUIRE(it != err.end()); REQUIRE(it->type() == error::OBJECT_MEMBER); auto&& e = static_cast(*it); REQUIRE(e.member_name() == "Third"); } } SECTION("Invalid enum", "[parsing], [error], [enum]") { REQUIRE( !from_json_file(get_base_dir() + "/examples/failure/invalid_enum.json", &users, &err)); CAPTURE(err.description()); REQUIRE(!err.error_stack().empty()); REQUIRE(err.begin()->type() == error::INVALID_ENUM); const auto& e = static_cast(*err.begin()); REQUIRE(e.description() == "\"West\" is an invalid enum name"); } } TEST_CASE("Test for max leaf number check", "[serialization]") { std::vector users, reparsed_users; ParseStatus err; bool success = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users.size() == 3); std::string json_output = to_pretty_json_string(users); CAPTURE(json_output); staticjson::GlobalConfig::getInstance()->setMaxLeaves(20); success = from_json_string(json_output.c_str(), &reparsed_users, &err); staticjson::GlobalConfig::getInstance()->unsetMaxLeavesFlag(); { CAPTURE(err.description()); REQUIRE(!success); } REQUIRE(err.description().find("Too many leaves") != std::string::npos); } TEST_CASE("Test for max depth check", "[serialization]") { std::vector users, reparsed_users; ParseStatus err; staticjson::GlobalConfig::getInstance()->setMaxDepth(3); bool success = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); staticjson::GlobalConfig::getInstance()->unsetMaxDepthFlag(); { CAPTURE(err.description()); REQUIRE(!success); } REQUIRE(err.description().find("Too many levels of recursion") != std::string::npos); } TEST_CASE("Test for writing JSON", "[serialization]") { std::vector users, reparsed_users; ParseStatus err; bool success = from_json_file(get_base_dir() + "/examples/success/user_array.json", &users, &err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users.size() == 3); std::string json_output = to_pretty_json_string(users); CAPTURE(json_output); success = from_json_string(json_output.c_str(), &reparsed_users, &err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(users == reparsed_users); } static bool is_valid_json(const std::string& filename, rapidjson::SchemaValidator* validator) { Document d; REQUIRE(from_json_file(filename, &d, nullptr)); bool rc = d.Accept(*validator); validator->Reset(); return rc; } TEST_CASE("Schema generation and validation") { const char* invalid_json_filenames[] = {"out_of_range.json", "integer_string.json", "missing_required.json", "map_element_mismatch.json", "null_in_key.json", "single_object.json", "unknown_field.json", "invalid_enum.json"}; { std::vector users; Document schema = export_json_schema(&users); rapidjson::SchemaDocument sd(schema); rapidjson::SchemaValidator validator(sd); REQUIRE(is_valid_json(get_base_dir() + "/examples/success/user_array.json", &validator)); REQUIRE(is_valid_json(get_base_dir() + "/examples/success/user_array_compact.json", &validator)); for (const char* fn : invalid_json_filenames) REQUIRE(!is_valid_json(get_base_dir() + "/examples/failure/" + fn, &validator)); } { std::map users; Document schema = export_json_schema(&users); rapidjson::SchemaDocument sd(schema); rapidjson::SchemaValidator validator(sd); REQUIRE(is_valid_json(get_base_dir() + "/examples/success/user_map.json", &validator)); for (const char* fn : invalid_json_filenames) REQUIRE(!is_valid_json(get_base_dir() + "/examples/failure/" + fn, &validator)); } } ================================================ FILE: test/test_memory_usage.cpp ================================================ #include #include "catch.hpp" using namespace staticjson; namespace { const std::string COMPLEX_VALUES = "complex_values"; struct Simple { std::vector floats; void staticjson_init(ObjectHandler* h) { h->add_property("floats", &floats); } }; struct Struct { std::string name; std::vector, std::list>> complex_values; Simple simple; void staticjson_init(ObjectHandler* h) { h->add_property("name", &name); h->add_property(COMPLEX_VALUES, &complex_values, staticjson::Flags::AllowDuplicateKey); h->add_property("simple", &simple); } }; } TEST_CASE("Test memory usage") { size_t memory_usage_before = 0, memory_usage_after = 0; std::string serialized; std::vector structs(100); for (size_t i = 0; i < structs.size(); ++i) { structs[i].name = "Struct" + std::to_string(i); structs[i].complex_values.resize(i); for (size_t j = 0; j < i; ++j) { auto&& v = structs[i].complex_values[j]; std::get<0>(v).push_back(j); for (size_t k = 0; k < 2 * j; ++k) { std::get<1>(v).emplace_back(std::to_string(k)); } } } Handler> h(&structs); serialized = nonpublic::serialize_json_string(&h); memory_usage_before = h.get_internal_handler().get_memory_pool().Capacity(); structs.clear(); h.prepare_for_reuse(); ParseStatus status; REQUIRE(nonpublic::parse_json_string(serialized.c_str(), &h, &status)); memory_usage_after = h.get_internal_handler().get_memory_pool().Capacity(); printf("Memory usage: before %zu, after %zu\n", memory_usage_before, memory_usage_after); CHECK(memory_usage_before == memory_usage_after); } ================================================ FILE: test/test_tensor.cpp ================================================ #include #include "catch.hpp" #include "myarray.hpp" #include #include #include #include using namespace staticjson; const std::string& get_base_dir(); TEST_CASE("Success tensor") { std::list, 3>> tensor; ParseStatus err; bool success = from_json_file(get_base_dir() + "/examples/success/tensor.json", &tensor, &err); { CAPTURE(err.description()); REQUIRE(success); } REQUIRE(tensor.size() == 4); REQUIRE(!tensor.back().empty()); REQUIRE(tensor.back().back().empty()); auto&& first = tensor.front(); REQUIRE(first.size() == 3); REQUIRE(first[0].size() == 3); REQUIRE(first[0][0] == 1); } TEST_CASE("Error tensor") { std::list, 2>> tensor; { ParseStatus err; REQUIRE(!from_json_file( get_base_dir() + "/examples/failure/tensor_type_mismatch.json", &tensor, &err)); REQUIRE(err.begin()->type() == error::TYPE_MISMATCH); } { ParseStatus err; REQUIRE(!from_json_file( get_base_dir() + "/examples/failure/tensor_length_error.json", &tensor, &err)); REQUIRE(err.begin()->type() == error::ARRAY_LENGTH_MISMATCH); } }