Merge branch 'develop' into feature/rfc7396
This commit is contained in:
commit
0e8f01a963
134 changed files with 20390 additions and 1770 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -21,3 +21,4 @@ benchmarks/files/numbers/*.json
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
|
|
||||||
test/test-*
|
test/test-*
|
||||||
|
third_party/Amalgamate
|
||||||
|
|
|
@ -4,18 +4,24 @@ cmake_minimum_required(VERSION 3.0.0)
|
||||||
## PROJECT
|
## PROJECT
|
||||||
## name and version
|
## name and version
|
||||||
##
|
##
|
||||||
project(nlohmann_json VERSION 2.1.1 LANGUAGES CXX)
|
project(nlohmann_json VERSION 3.0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
##
|
||||||
|
## INCLUDE
|
||||||
|
##
|
||||||
|
##
|
||||||
|
include(ExternalProject)
|
||||||
|
|
||||||
##
|
##
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
##
|
##
|
||||||
option(JSON_BuildTests "Build the unit tests" ON)
|
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON)
|
||||||
|
|
||||||
##
|
##
|
||||||
## CONFIGURATION
|
## CONFIGURATION
|
||||||
##
|
##
|
||||||
set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME})
|
set(NLOHMANN_JSON_TARGET_NAME ${PROJECT_NAME})
|
||||||
set(NLOHMANN_JSON_SOURCE_DIR "src/")
|
set(NLOHMANN_JSON_SOURCE_DIR "src")
|
||||||
set(NLOHMANN_JSON_CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
|
set(NLOHMANN_JSON_CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
|
||||||
set(NLOHMANN_JSON_INCLUDE_INSTALL_DIR "include")
|
set(NLOHMANN_JSON_INCLUDE_INSTALL_DIR "include")
|
||||||
set(NLOHMANN_JSON_HEADER_INSTALL_DIR "${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}/nlohmann")
|
set(NLOHMANN_JSON_HEADER_INSTALL_DIR "${NLOHMANN_JSON_INCLUDE_INSTALL_DIR}/nlohmann")
|
||||||
|
@ -54,7 +60,9 @@ endif()
|
||||||
## TESTS
|
## TESTS
|
||||||
## create and configure the unit test target
|
## create and configure the unit test target
|
||||||
##
|
##
|
||||||
if(JSON_BuildTests)
|
include(CTest) #adds option BUILD_TESTING (default ON)
|
||||||
|
|
||||||
|
if(BUILD_TESTING AND JSON_BuildTests)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
include_directories(${NLOHMANN_JSON_SOURCE_DIR})
|
include_directories(${NLOHMANN_JSON_SOURCE_DIR})
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
|
420
ChangeLog.md
420
ChangeLog.md
|
@ -1,6 +1,425 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [v3.0.1](https://github.com/nlohmann/json/releases/tag/v3.0.1) (2017-12-29)
|
||||||
|
[Full Changelog](https://github.com/nlohmann/json/compare/v3.0.0...v3.0.1)
|
||||||
|
|
||||||
|
- Problem parsing array to global vector [\#896](https://github.com/nlohmann/json/issues/896)
|
||||||
|
- Invalid RFC6902 copy operation succeeds [\#894](https://github.com/nlohmann/json/issues/894)
|
||||||
|
- How to rename a key during looping? [\#893](https://github.com/nlohmann/json/issues/893)
|
||||||
|
- clang++-6.0 \(6.0.0-svn321357-1\) warning [\#892](https://github.com/nlohmann/json/issues/892)
|
||||||
|
- Make json.hpp aware of the modules TS? [\#891](https://github.com/nlohmann/json/issues/891)
|
||||||
|
- All enum values not handled in switch cases. \( -Wswitch-enum \) [\#889](https://github.com/nlohmann/json/issues/889)
|
||||||
|
- JSON Pointer resolve failure resulting in incorrect exception code [\#888](https://github.com/nlohmann/json/issues/888)
|
||||||
|
- Unexpected nested arrays from std::vector [\#886](https://github.com/nlohmann/json/issues/886)
|
||||||
|
- erase multiple elements from a json object [\#884](https://github.com/nlohmann/json/issues/884)
|
||||||
|
- Container function overview in Doxygen is not updated [\#883](https://github.com/nlohmann/json/issues/883)
|
||||||
|
- How to use this for binary file uploads [\#881](https://github.com/nlohmann/json/issues/881)
|
||||||
|
- Allow setting JSON\_BuildTests=OFF from parent CMakeLists.txt [\#846](https://github.com/nlohmann/json/issues/846)
|
||||||
|
- Unit test fails for local-independent str-to-num [\#845](https://github.com/nlohmann/json/issues/845)
|
||||||
|
- Another idea about type support [\#774](https://github.com/nlohmann/json/issues/774)
|
||||||
|
|
||||||
|
- Includes CTest module/adds BUILD\_TESTING option [\#885](https://github.com/nlohmann/json/pull/885) ([TinyTinni](https://github.com/TinyTinni))
|
||||||
|
- Fix MSVC warning C4819 [\#882](https://github.com/nlohmann/json/pull/882) ([erengy](https://github.com/erengy))
|
||||||
|
- Merge branch 'develop' into coverity\_scan [\#880](https://github.com/nlohmann/json/pull/880) ([nlohmann](https://github.com/nlohmann))
|
||||||
|
- :wrench: Fix up a few more effc++ items [\#858](https://github.com/nlohmann/json/pull/858) ([mattismyname](https://github.com/mattismyname))
|
||||||
|
|
||||||
|
## [v3.0.0](https://github.com/nlohmann/json/releases/tag/v3.0.0) (2017-12-17)
|
||||||
|
[Full Changelog](https://github.com/nlohmann/json/compare/v2.1.1...v3.0.0)
|
||||||
|
|
||||||
|
- unicode strings [\#878](https://github.com/nlohmann/json/issues/878)
|
||||||
|
- Visual Studio 2017 15.5 C++17 std::allocator deprecations [\#872](https://github.com/nlohmann/json/issues/872)
|
||||||
|
- Typo "excpetion" [\#869](https://github.com/nlohmann/json/issues/869)
|
||||||
|
- Explicit array example in README.md incorrect [\#867](https://github.com/nlohmann/json/issues/867)
|
||||||
|
- why don't you release this from Feb. ? [\#865](https://github.com/nlohmann/json/issues/865)
|
||||||
|
- json::parse throws std::invalid\_argument when processing string generated by json::dump\(\) [\#863](https://github.com/nlohmann/json/issues/863)
|
||||||
|
- code analysis: potential bug? [\#859](https://github.com/nlohmann/json/issues/859)
|
||||||
|
- MSVC2017, 15.5 new issues. [\#857](https://github.com/nlohmann/json/issues/857)
|
||||||
|
- very basic: fetching string value/content without quotes [\#853](https://github.com/nlohmann/json/issues/853)
|
||||||
|
- Ambiguous function call to get with pointer type and constant json object in VS2015 \(15.4.4\) [\#852](https://github.com/nlohmann/json/issues/852)
|
||||||
|
- How to put object in the array as a member? [\#850](https://github.com/nlohmann/json/issues/850)
|
||||||
|
- misclick, please ignore [\#849](https://github.com/nlohmann/json/issues/849)
|
||||||
|
- Make XML great again. [\#847](https://github.com/nlohmann/json/issues/847)
|
||||||
|
- Converting to array not working [\#843](https://github.com/nlohmann/json/issues/843)
|
||||||
|
- Iteration weirdness [\#842](https://github.com/nlohmann/json/issues/842)
|
||||||
|
- Use reference or pointer as Object value [\#841](https://github.com/nlohmann/json/issues/841)
|
||||||
|
- Ambiguity in parsing nested maps [\#840](https://github.com/nlohmann/json/issues/840)
|
||||||
|
- could not find from\_json\(\) method in T's namespace [\#839](https://github.com/nlohmann/json/issues/839)
|
||||||
|
- Incorrect parse error with binary data in keys? [\#838](https://github.com/nlohmann/json/issues/838)
|
||||||
|
- using dump\(\) when std::wstring is StringType with VS2017 [\#836](https://github.com/nlohmann/json/issues/836)
|
||||||
|
- Show the path of the currently parsed value when an error occurs [\#835](https://github.com/nlohmann/json/issues/835)
|
||||||
|
- Repetitive data type while reading [\#833](https://github.com/nlohmann/json/issues/833)
|
||||||
|
- Stack-overflow \(OSS-Fuzz 4234\) [\#832](https://github.com/nlohmann/json/issues/832)
|
||||||
|
- Storing multiple types inside map [\#831](https://github.com/nlohmann/json/issues/831)
|
||||||
|
- Application terminating [\#830](https://github.com/nlohmann/json/issues/830)
|
||||||
|
- Missing CMake hunter package? [\#828](https://github.com/nlohmann/json/issues/828)
|
||||||
|
- std::map\<std::string, std::string\> from json object yields C2665: 'std::pair\<const \_Kty,\_Ty\>::pair': none of the 2 overloads could convert all the argument types [\#827](https://github.com/nlohmann/json/issues/827)
|
||||||
|
- object.dump gives quoted string, want to use .dump\(\) to generate javascripts. [\#826](https://github.com/nlohmann/json/issues/826)
|
||||||
|
- Assertion failed on \["NoExistKey"\] of an not existing key of const json& [\#825](https://github.com/nlohmann/json/issues/825)
|
||||||
|
- vs2015 error : static member will remain uninitialized at runtime but use in constant-expressions is supported [\#824](https://github.com/nlohmann/json/issues/824)
|
||||||
|
- Code Checking Warnings from json.hpp on VS2017 Community [\#821](https://github.com/nlohmann/json/issues/821)
|
||||||
|
- Missing iostream in try online [\#820](https://github.com/nlohmann/json/issues/820)
|
||||||
|
- Floating point value loses decimal point during dump [\#818](https://github.com/nlohmann/json/issues/818)
|
||||||
|
- Conan package for the library [\#817](https://github.com/nlohmann/json/issues/817)
|
||||||
|
- stream error [\#815](https://github.com/nlohmann/json/issues/815)
|
||||||
|
- Link error when using find\(\) on the latest commit [\#814](https://github.com/nlohmann/json/issues/814)
|
||||||
|
- ABI issue with json object between 2 shared libraries [\#813](https://github.com/nlohmann/json/issues/813)
|
||||||
|
- scan\_string\(\) return token\_type::parse\_error; when parse ansi file [\#812](https://github.com/nlohmann/json/issues/812)
|
||||||
|
- segfault when using fifo\_map with json [\#810](https://github.com/nlohmann/json/issues/810)
|
||||||
|
- This shit is shit [\#809](https://github.com/nlohmann/json/issues/809)
|
||||||
|
- \_finite and \_isnan are no members of "std" [\#808](https://github.com/nlohmann/json/issues/808)
|
||||||
|
- how to print out the line which causing exception? [\#806](https://github.com/nlohmann/json/issues/806)
|
||||||
|
- {} uses copy constructor, while = does not [\#805](https://github.com/nlohmann/json/issues/805)
|
||||||
|
- json.hpp:8955: multiple definition of function that is not defined twice or more. [\#804](https://github.com/nlohmann/json/issues/804)
|
||||||
|
- \[question\] to\_json for base and derived class [\#803](https://github.com/nlohmann/json/issues/803)
|
||||||
|
- Misleading error message - unexpected '"' - on incorrect utf-8 symbol [\#802](https://github.com/nlohmann/json/issues/802)
|
||||||
|
- json data = std::string\_view\("hi"\); doesn't work? [\#801](https://github.com/nlohmann/json/issues/801)
|
||||||
|
- Thread safety of parse\(\) [\#800](https://github.com/nlohmann/json/issues/800)
|
||||||
|
- Numbers as strings [\#799](https://github.com/nlohmann/json/issues/799)
|
||||||
|
- Tests failing on arm [\#797](https://github.com/nlohmann/json/issues/797)
|
||||||
|
- Using your library \(without modification\) in another library [\#796](https://github.com/nlohmann/json/issues/796)
|
||||||
|
- Iterating over sub-object [\#794](https://github.com/nlohmann/json/issues/794)
|
||||||
|
- how to get the json object again from which printed by the method of dump\(\) [\#792](https://github.com/nlohmann/json/issues/792)
|
||||||
|
- ppa to include source [\#791](https://github.com/nlohmann/json/issues/791)
|
||||||
|
- Different include paths in macOS and Ubuntu [\#790](https://github.com/nlohmann/json/issues/790)
|
||||||
|
- Missing break after line 12886 in switch/case [\#789](https://github.com/nlohmann/json/issues/789)
|
||||||
|
- All unit tests fail? [\#787](https://github.com/nlohmann/json/issues/787)
|
||||||
|
- More use of move semantics in deserialization [\#786](https://github.com/nlohmann/json/issues/786)
|
||||||
|
- warning C4706 - Visual Studio 2017 \(/W4\) [\#784](https://github.com/nlohmann/json/issues/784)
|
||||||
|
- Compile error in clang 5.0 [\#782](https://github.com/nlohmann/json/issues/782)
|
||||||
|
- Error Installing appium\_lib with Ruby v2.4.2 Due to JSON [\#781](https://github.com/nlohmann/json/issues/781)
|
||||||
|
- ::get\<int\>\(\) fails in new\(er\) release \[MSVC\] [\#780](https://github.com/nlohmann/json/issues/780)
|
||||||
|
- Type Conversion [\#779](https://github.com/nlohmann/json/issues/779)
|
||||||
|
- Segfault on nested parsing [\#778](https://github.com/nlohmann/json/issues/778)
|
||||||
|
- Build warnings: shadowing exception id [\#776](https://github.com/nlohmann/json/issues/776)
|
||||||
|
- multi-level JSON support. [\#775](https://github.com/nlohmann/json/issues/775)
|
||||||
|
- SIGABRT on dump\(\) [\#773](https://github.com/nlohmann/json/issues/773)
|
||||||
|
- \[Question\] Custom StringType template parameter \(possibility for a KeyType template parameter\) [\#772](https://github.com/nlohmann/json/issues/772)
|
||||||
|
- constexpr ALL the Things! [\#771](https://github.com/nlohmann/json/issues/771)
|
||||||
|
- error: ‘BasicJsonType’ in namespace ‘::’ does not name a type [\#770](https://github.com/nlohmann/json/issues/770)
|
||||||
|
- Program calls abort function [\#769](https://github.com/nlohmann/json/issues/769)
|
||||||
|
- \[Question\] Floating point resolution config during dump\(\) ? [\#768](https://github.com/nlohmann/json/issues/768)
|
||||||
|
- make check - no test ran [\#767](https://github.com/nlohmann/json/issues/767)
|
||||||
|
- The library cannot work properly with custom allocator based containers [\#766](https://github.com/nlohmann/json/issues/766)
|
||||||
|
- Documentation or feature request. [\#763](https://github.com/nlohmann/json/issues/763)
|
||||||
|
- warnings in msvc about mix/max macro while windows.h is used in the project [\#762](https://github.com/nlohmann/json/issues/762)
|
||||||
|
- std::signbit ambiguous [\#761](https://github.com/nlohmann/json/issues/761)
|
||||||
|
- How to use value for std::experimental::optional type? [\#760](https://github.com/nlohmann/json/issues/760)
|
||||||
|
- Cannot load json file properly [\#759](https://github.com/nlohmann/json/issues/759)
|
||||||
|
- Compilation error with unordered\_map\< int, int \> [\#758](https://github.com/nlohmann/json/issues/758)
|
||||||
|
- CBOR string [\#757](https://github.com/nlohmann/json/issues/757)
|
||||||
|
- Proposal: out\_of\_range should be a subclass of std::out\_of\_range [\#756](https://github.com/nlohmann/json/issues/756)
|
||||||
|
- Compiling with icpc [\#755](https://github.com/nlohmann/json/issues/755)
|
||||||
|
- Getter is setting the value to null if the key does not exist [\#754](https://github.com/nlohmann/json/issues/754)
|
||||||
|
- parsing works sometimes and crashes others [\#752](https://github.com/nlohmann/json/issues/752)
|
||||||
|
- Static\_assert failed "incompatible pointer type" with Xcode [\#751](https://github.com/nlohmann/json/issues/751)
|
||||||
|
- user-defined literal operator not found [\#750](https://github.com/nlohmann/json/issues/750)
|
||||||
|
- getting clean string from it.key\(\) [\#748](https://github.com/nlohmann/json/issues/748)
|
||||||
|
- Best method for exploring and obtaining values of nested json objects when the names are not known beforehand? [\#747](https://github.com/nlohmann/json/issues/747)
|
||||||
|
- null char at the end of string [\#746](https://github.com/nlohmann/json/issues/746)
|
||||||
|
- Incorrect sample for operator \>\> in docs [\#745](https://github.com/nlohmann/json/issues/745)
|
||||||
|
- User-friendly documentation [\#744](https://github.com/nlohmann/json/issues/744)
|
||||||
|
- Retrieve all values that match a json path [\#743](https://github.com/nlohmann/json/issues/743)
|
||||||
|
- Compilation issue with gcc 7.2 [\#742](https://github.com/nlohmann/json/issues/742)
|
||||||
|
- CMake target nlohmann\_json does not have src into its interface includes [\#741](https://github.com/nlohmann/json/issues/741)
|
||||||
|
- Error when serializing empty json: type must be string, but is object [\#740](https://github.com/nlohmann/json/issues/740)
|
||||||
|
- Conversion error for std::map\<int, std::string\> [\#739](https://github.com/nlohmann/json/issues/739)
|
||||||
|
- Dumping Json to file as array [\#738](https://github.com/nlohmann/json/issues/738)
|
||||||
|
- nesting json objects [\#737](https://github.com/nlohmann/json/issues/737)
|
||||||
|
- where to find general help? [\#736](https://github.com/nlohmann/json/issues/736)
|
||||||
|
- Compilation Error on Clang 5.0 Upgrade [\#735](https://github.com/nlohmann/json/issues/735)
|
||||||
|
- Compilation error with std::map\<std::string, std::string\> on vs 2015 [\#734](https://github.com/nlohmann/json/issues/734)
|
||||||
|
- Benchmarks for Binary formats [\#733](https://github.com/nlohmann/json/issues/733)
|
||||||
|
- Move test blobs to a submodule? [\#732](https://github.com/nlohmann/json/issues/732)
|
||||||
|
- Support \n symbols in json string. [\#731](https://github.com/nlohmann/json/issues/731)
|
||||||
|
- Project's name is too generic and hard to search for [\#730](https://github.com/nlohmann/json/issues/730)
|
||||||
|
- Visual Studio 2015 IntelliTrace problems [\#729](https://github.com/nlohmann/json/issues/729)
|
||||||
|
- How to erase nested objects inside other objects? [\#728](https://github.com/nlohmann/json/issues/728)
|
||||||
|
- How to prevent alphabetical sorting of data? [\#727](https://github.com/nlohmann/json/issues/727)
|
||||||
|
- Serialization for CBOR [\#726](https://github.com/nlohmann/json/issues/726)
|
||||||
|
- Using json Object as value in a map [\#725](https://github.com/nlohmann/json/issues/725)
|
||||||
|
- std::regex and nlohmann::json value [\#724](https://github.com/nlohmann/json/issues/724)
|
||||||
|
- Warnings when compiling with VisualStudio 2015 [\#723](https://github.com/nlohmann/json/issues/723)
|
||||||
|
- Has this lib the unicode \(wstring\) support? [\#722](https://github.com/nlohmann/json/issues/722)
|
||||||
|
- When will be 3.0 in master? [\#721](https://github.com/nlohmann/json/issues/721)
|
||||||
|
- Determine the type from error message. [\#720](https://github.com/nlohmann/json/issues/720)
|
||||||
|
- Compile-Error C2100 \(MS VS2015\) in line 887 json.hpp [\#719](https://github.com/nlohmann/json/issues/719)
|
||||||
|
- from\_json not working for boost::optional example [\#718](https://github.com/nlohmann/json/issues/718)
|
||||||
|
- about from\_json and to\_json function [\#717](https://github.com/nlohmann/json/issues/717)
|
||||||
|
- How to deserialize array with derived objects [\#716](https://github.com/nlohmann/json/issues/716)
|
||||||
|
- How to detect parse failure? [\#715](https://github.com/nlohmann/json/issues/715)
|
||||||
|
- Parse throw std::ios\_base::failure exception when failbit set to true [\#714](https://github.com/nlohmann/json/issues/714)
|
||||||
|
- Is there a way of format just making a pretty print without changing the key's orders ? [\#713](https://github.com/nlohmann/json/issues/713)
|
||||||
|
- Serialization of array of not same model items [\#712](https://github.com/nlohmann/json/issues/712)
|
||||||
|
- pointer to json parse vector [\#711](https://github.com/nlohmann/json/issues/711)
|
||||||
|
- Gtest SEH Exception [\#709](https://github.com/nlohmann/json/issues/709)
|
||||||
|
- broken from\_json implementation for pair and tuple [\#707](https://github.com/nlohmann/json/issues/707)
|
||||||
|
- Unevaluated lambda in assert breaks gcc 7 build [\#705](https://github.com/nlohmann/json/issues/705)
|
||||||
|
- Issues when adding values to firebase database [\#704](https://github.com/nlohmann/json/issues/704)
|
||||||
|
- Floating point equality - revisited [\#703](https://github.com/nlohmann/json/issues/703)
|
||||||
|
- Conversion from valarray\<double\> to json fails to build [\#702](https://github.com/nlohmann/json/issues/702)
|
||||||
|
- internal compiler error \(gcc7\) [\#701](https://github.com/nlohmann/json/issues/701)
|
||||||
|
- One build system to rule them all [\#698](https://github.com/nlohmann/json/issues/698)
|
||||||
|
- Generated nlohmann\_jsonConfig.cmake does not set JSON\_INCLUDE\_DIR [\#695](https://github.com/nlohmann/json/issues/695)
|
||||||
|
- support the Chinese language in json string [\#694](https://github.com/nlohmann/json/issues/694)
|
||||||
|
- NaN problem within develop branch [\#693](https://github.com/nlohmann/json/issues/693)
|
||||||
|
- Please post example of specialization for boost::filesystem [\#692](https://github.com/nlohmann/json/issues/692)
|
||||||
|
- Impossible to do an array of composite objects [\#691](https://github.com/nlohmann/json/issues/691)
|
||||||
|
- How to save json to file? [\#690](https://github.com/nlohmann/json/issues/690)
|
||||||
|
- my simple json parser [\#689](https://github.com/nlohmann/json/issues/689)
|
||||||
|
- problem with new struct parsing syntax [\#688](https://github.com/nlohmann/json/issues/688)
|
||||||
|
- Parse error while parse the json string contains UTF 8 encoded document bytes string [\#684](https://github.com/nlohmann/json/issues/684)
|
||||||
|
- \[question\] how to get a string value by pointer [\#683](https://github.com/nlohmann/json/issues/683)
|
||||||
|
- create json object from string variable [\#681](https://github.com/nlohmann/json/issues/681)
|
||||||
|
- adl\_serializer and CRTP [\#680](https://github.com/nlohmann/json/issues/680)
|
||||||
|
- Is there a way to control the precision of serialized floating point numbers? [\#677](https://github.com/nlohmann/json/issues/677)
|
||||||
|
- Is there a way to get the path of a value? [\#676](https://github.com/nlohmann/json/issues/676)
|
||||||
|
- Could the parser locate errors to line? [\#675](https://github.com/nlohmann/json/issues/675)
|
||||||
|
- There is performance inefficiency found by coverity tool json2.1.1/include/nlohmann/json.hpp [\#673](https://github.com/nlohmann/json/issues/673)
|
||||||
|
- include problem, when cmake on osx [\#672](https://github.com/nlohmann/json/issues/672)
|
||||||
|
- Operator= ambiguous in C++1z and GCC 7.1.1 [\#670](https://github.com/nlohmann/json/issues/670)
|
||||||
|
- should't the cmake install target be to nlohman/json.hpp [\#668](https://github.com/nlohmann/json/issues/668)
|
||||||
|
- deserialise from `std::vector` [\#667](https://github.com/nlohmann/json/issues/667)
|
||||||
|
- How to iterate? [\#665](https://github.com/nlohmann/json/issues/665)
|
||||||
|
- could this json lib work on windows? [\#664](https://github.com/nlohmann/json/issues/664)
|
||||||
|
- How does from\_json work? [\#662](https://github.com/nlohmann/json/issues/662)
|
||||||
|
- insert\(or merge\) object should replace same key , not ignore [\#661](https://github.com/nlohmann/json/issues/661)
|
||||||
|
- Why is an object ordering values by Alphabetical Order? [\#660](https://github.com/nlohmann/json/issues/660)
|
||||||
|
- Parse method doesn't handle newlines. [\#659](https://github.com/nlohmann/json/issues/659)
|
||||||
|
- Compilation "note" on GCC 6 ARM [\#658](https://github.com/nlohmann/json/issues/658)
|
||||||
|
- Adding additional push\_back/operator+= rvalue overloads for JSON object [\#657](https://github.com/nlohmann/json/issues/657)
|
||||||
|
- dump's parameter "ensure\_ascii" creates too long sequences [\#656](https://github.com/nlohmann/json/issues/656)
|
||||||
|
- Question: parsing `void \*` [\#655](https://github.com/nlohmann/json/issues/655)
|
||||||
|
- how should I check a string is valid JSON string ? [\#653](https://github.com/nlohmann/json/issues/653)
|
||||||
|
- Question: thread safety of read only accesses [\#651](https://github.com/nlohmann/json/issues/651)
|
||||||
|
- Eclipse: Method 'size' could not be resolved [\#649](https://github.com/nlohmann/json/issues/649)
|
||||||
|
- Update/Add object fields [\#648](https://github.com/nlohmann/json/issues/648)
|
||||||
|
- No exception raised for Out Of Range input of numbers [\#647](https://github.com/nlohmann/json/issues/647)
|
||||||
|
- Package Name [\#646](https://github.com/nlohmann/json/issues/646)
|
||||||
|
- What is the meaning of operator\[\]\(T\* key\) [\#645](https://github.com/nlohmann/json/issues/645)
|
||||||
|
- Which is the correct way to json objects as parameters to functions? [\#644](https://github.com/nlohmann/json/issues/644)
|
||||||
|
- Method to get string representations of values [\#642](https://github.com/nlohmann/json/issues/642)
|
||||||
|
- CBOR serialization of a given JSON value does not serialize [\#641](https://github.com/nlohmann/json/issues/641)
|
||||||
|
- Are we forced to use "-fexceptions" flag in android ndk project [\#640](https://github.com/nlohmann/json/issues/640)
|
||||||
|
- Comparison of objects containing floats [\#639](https://github.com/nlohmann/json/issues/639)
|
||||||
|
- 'localeconv' is not supported by NDK for SDK \<=20 [\#638](https://github.com/nlohmann/json/issues/638)
|
||||||
|
- \[Question\] cLion integration [\#637](https://github.com/nlohmann/json/issues/637)
|
||||||
|
- How to construct an iteratable usage in nlohmann json? [\#636](https://github.com/nlohmann/json/issues/636)
|
||||||
|
- \[Question\] copy assign json-container to vector [\#635](https://github.com/nlohmann/json/issues/635)
|
||||||
|
- Get size without .dump\(\) [\#634](https://github.com/nlohmann/json/issues/634)
|
||||||
|
- Segmentation fault when parsing invalid json file [\#633](https://github.com/nlohmann/json/issues/633)
|
||||||
|
- How to serialize from json to vector\<customType\>? [\#632](https://github.com/nlohmann/json/issues/632)
|
||||||
|
- no member named 'thousands\_sep' in 'lconv' [\#631](https://github.com/nlohmann/json/issues/631)
|
||||||
|
- \[Question\] Any fork for \(the unsupported\) Visual Studio 2012 version? [\#628](https://github.com/nlohmann/json/issues/628)
|
||||||
|
- Dependency injection in serializer [\#627](https://github.com/nlohmann/json/issues/627)
|
||||||
|
- from\_json for std::array [\#625](https://github.com/nlohmann/json/issues/625)
|
||||||
|
- Discussion: How to structure the parsing function families [\#623](https://github.com/nlohmann/json/issues/623)
|
||||||
|
- Question: How to erase subtree [\#622](https://github.com/nlohmann/json/issues/622)
|
||||||
|
- Insertion into nested json field [\#621](https://github.com/nlohmann/json/issues/621)
|
||||||
|
- \[Question\] When using this as git submodule, will it clone the whole thing include test data and benchmark? [\#620](https://github.com/nlohmann/json/issues/620)
|
||||||
|
- Question: return static json object from function [\#618](https://github.com/nlohmann/json/issues/618)
|
||||||
|
- icc16 error [\#617](https://github.com/nlohmann/json/issues/617)
|
||||||
|
- \[-Wdeprecated-declarations\] in row `j \>\> ss;` in file `json.hpp:7405:26` and FAILED unit tests with MinGWx64! [\#616](https://github.com/nlohmann/json/issues/616)
|
||||||
|
- to\_json for pairs, tuples [\#614](https://github.com/nlohmann/json/issues/614)
|
||||||
|
- Using uninitialized memory 'buf' in line 11173 v2.1.1? [\#613](https://github.com/nlohmann/json/issues/613)
|
||||||
|
- How to parse multiple same Keys of JSON and save them? [\#612](https://github.com/nlohmann/json/issues/612)
|
||||||
|
- "Multiple declarations" error when using types defined with `typedef` [\#611](https://github.com/nlohmann/json/issues/611)
|
||||||
|
- 2.1.1+ breaks compilation of shared\_ptr\<json\> == 0 [\#610](https://github.com/nlohmann/json/issues/610)
|
||||||
|
- a bug of inheritance ? [\#608](https://github.com/nlohmann/json/issues/608)
|
||||||
|
- std::map key conversion with to\_json [\#607](https://github.com/nlohmann/json/issues/607)
|
||||||
|
- json.hpp:6384:62: error: wrong number of template arguments \(1, should be 2\) [\#606](https://github.com/nlohmann/json/issues/606)
|
||||||
|
- Incremental parsing: Where's the push version? [\#605](https://github.com/nlohmann/json/issues/605)
|
||||||
|
- Is there a way to validate the structure of a json object ? [\#604](https://github.com/nlohmann/json/issues/604)
|
||||||
|
- \[Question\] Issue when using Appveyor when compiling library [\#603](https://github.com/nlohmann/json/issues/603)
|
||||||
|
- BOM not skipped when using json:parse\(iterator\) [\#602](https://github.com/nlohmann/json/issues/602)
|
||||||
|
- Use of the binary type in CBOR and Message Pack [\#601](https://github.com/nlohmann/json/issues/601)
|
||||||
|
- Newbie issue: how does one convert a map in Json back to std::map? [\#600](https://github.com/nlohmann/json/issues/600)
|
||||||
|
- Plugin system [\#599](https://github.com/nlohmann/json/issues/599)
|
||||||
|
- Feature request: Comments [\#597](https://github.com/nlohmann/json/issues/597)
|
||||||
|
- Using custom types for scalars? [\#596](https://github.com/nlohmann/json/issues/596)
|
||||||
|
- Issues with the arithmetic in iterator and reverse iterator [\#593](https://github.com/nlohmann/json/issues/593)
|
||||||
|
- not enough examples [\#592](https://github.com/nlohmann/json/issues/592)
|
||||||
|
- in-class initialization for type 'const T' is not yet implemented [\#591](https://github.com/nlohmann/json/issues/591)
|
||||||
|
- compiling with gcc 7 -\> error on bool operator \< [\#590](https://github.com/nlohmann/json/issues/590)
|
||||||
|
- Parsing from stream leads to an array [\#589](https://github.com/nlohmann/json/issues/589)
|
||||||
|
- Buggy support for binary string data [\#587](https://github.com/nlohmann/json/issues/587)
|
||||||
|
- C++17's ambiguous conversion [\#586](https://github.com/nlohmann/json/issues/586)
|
||||||
|
- How does the messagepack encoding/decoding compare to msgpack-cpp in terms of performance? [\#585](https://github.com/nlohmann/json/issues/585)
|
||||||
|
- is it possible to check existence of a value deep in hierarchy? [\#584](https://github.com/nlohmann/json/issues/584)
|
||||||
|
- loading from a stream and exceptions [\#582](https://github.com/nlohmann/json/issues/582)
|
||||||
|
- Visual Studio seems not to have all min\(\) function versions [\#581](https://github.com/nlohmann/json/issues/581)
|
||||||
|
- Supporting of the json schema [\#580](https://github.com/nlohmann/json/issues/580)
|
||||||
|
- Stack-overflow \(OSS-Fuzz 1444\) [\#577](https://github.com/nlohmann/json/issues/577)
|
||||||
|
- Heap-buffer-overflow \(OSS-Fuzz 1400\) [\#575](https://github.com/nlohmann/json/issues/575)
|
||||||
|
- JSON escape quotes [\#574](https://github.com/nlohmann/json/issues/574)
|
||||||
|
- error: static\_assert failed [\#573](https://github.com/nlohmann/json/issues/573)
|
||||||
|
- Storing floats, and round trip serialisation/deserialisation diffs [\#572](https://github.com/nlohmann/json/issues/572)
|
||||||
|
- JSON.getLong produces inconsistent results [\#571](https://github.com/nlohmann/json/issues/571)
|
||||||
|
- Request: Object.at\(\) with default return value [\#570](https://github.com/nlohmann/json/issues/570)
|
||||||
|
- Internal structure gets corrupted while parsing [\#569](https://github.com/nlohmann/json/issues/569)
|
||||||
|
- create template \<typename Iter\> basic\_json from\_cbor\(Iter begin, Iter end\) [\#568](https://github.com/nlohmann/json/issues/568)
|
||||||
|
- Need to improve ignores.. [\#567](https://github.com/nlohmann/json/issues/567)
|
||||||
|
- Conan.io [\#566](https://github.com/nlohmann/json/issues/566)
|
||||||
|
- contradictory documentation regarding json::find [\#565](https://github.com/nlohmann/json/issues/565)
|
||||||
|
- Unexpected '\"' in middle of array [\#564](https://github.com/nlohmann/json/issues/564)
|
||||||
|
- Support parse std::pair to Json object [\#563](https://github.com/nlohmann/json/issues/563)
|
||||||
|
- json and Microsoft Visual c++ Compiler Nov 2012 CTP [\#562](https://github.com/nlohmann/json/issues/562)
|
||||||
|
- from\_json declaration order and exceptions [\#561](https://github.com/nlohmann/json/issues/561)
|
||||||
|
- Tip: Don't upgrade to VS2017 if using json initializer list constructs [\#559](https://github.com/nlohmann/json/issues/559)
|
||||||
|
- parse error - unexpected end of input [\#558](https://github.com/nlohmann/json/issues/558)
|
||||||
|
- Cant modify existing numbers inside a json object [\#557](https://github.com/nlohmann/json/issues/557)
|
||||||
|
- Minimal repository \(current size very large\) [\#556](https://github.com/nlohmann/json/issues/556)
|
||||||
|
- Better support for SAX style serialize and deserialize in new version? [\#554](https://github.com/nlohmann/json/issues/554)
|
||||||
|
- Cannot convert from json array to std::array [\#553](https://github.com/nlohmann/json/issues/553)
|
||||||
|
- Do not define an unnamed namespace in a header file \(DCL59-CPP\) [\#552](https://github.com/nlohmann/json/issues/552)
|
||||||
|
- Parse error on known good json file [\#551](https://github.com/nlohmann/json/issues/551)
|
||||||
|
- Warning on Intel compiler \(icc 17\) [\#550](https://github.com/nlohmann/json/issues/550)
|
||||||
|
- multiple versions of 'vsnprintf' [\#549](https://github.com/nlohmann/json/issues/549)
|
||||||
|
- illegal indirection [\#548](https://github.com/nlohmann/json/issues/548)
|
||||||
|
- Ambiguous compare operators with clang-5.0 [\#547](https://github.com/nlohmann/json/issues/547)
|
||||||
|
- Using tsl::ordered\_map [\#546](https://github.com/nlohmann/json/issues/546)
|
||||||
|
- Compiler support errors are inconvenient [\#544](https://github.com/nlohmann/json/issues/544)
|
||||||
|
- Head Elements Sorting [\#543](https://github.com/nlohmann/json/issues/543)
|
||||||
|
- Duplicate symbols error happens while to\_json/from\_json method implemented inside entity definition header file [\#542](https://github.com/nlohmann/json/issues/542)
|
||||||
|
- consider adding a bool json::is\_valid\(std::string const&\) non-member function [\#541](https://github.com/nlohmann/json/issues/541)
|
||||||
|
- Help request [\#539](https://github.com/nlohmann/json/issues/539)
|
||||||
|
- How to deal with missing keys in `from\_json`? [\#538](https://github.com/nlohmann/json/issues/538)
|
||||||
|
- recursive from\_msgpack implementation will stack overflow [\#537](https://github.com/nlohmann/json/issues/537)
|
||||||
|
- Exception objects must be nothrow copy constructible \(ERR60-CPP\) [\#531](https://github.com/nlohmann/json/issues/531)
|
||||||
|
- Support for multiple root elements [\#529](https://github.com/nlohmann/json/issues/529)
|
||||||
|
- Port has\_shape from dropbox/json11 [\#528](https://github.com/nlohmann/json/issues/528)
|
||||||
|
- dump\_float: truncation from ptrdiff\_t to long [\#527](https://github.com/nlohmann/json/issues/527)
|
||||||
|
- Make exception base class visible in basic\_json [\#525](https://github.com/nlohmann/json/issues/525)
|
||||||
|
- msgpack unit test failures on ppc64 arch [\#524](https://github.com/nlohmann/json/issues/524)
|
||||||
|
- How about split the implementation out, and only leave the interface? [\#523](https://github.com/nlohmann/json/issues/523)
|
||||||
|
- VC++2017 not enough actual parameters for macro 'max' [\#522](https://github.com/nlohmann/json/issues/522)
|
||||||
|
- crash on empty ifstream [\#521](https://github.com/nlohmann/json/issues/521)
|
||||||
|
- Suggestion: Support tabs for indentation when serializing to stream. [\#520](https://github.com/nlohmann/json/issues/520)
|
||||||
|
- Abrt in get\_number \(OSS-Fuzz 885\) [\#519](https://github.com/nlohmann/json/issues/519)
|
||||||
|
- Abrt on unknown address \(OSS-Fuzz 884\) [\#518](https://github.com/nlohmann/json/issues/518)
|
||||||
|
- Stack-overflow \(OSS-Fuzz 869\) [\#517](https://github.com/nlohmann/json/issues/517)
|
||||||
|
- Assertion error \(OSS-Fuzz 868\) [\#516](https://github.com/nlohmann/json/issues/516)
|
||||||
|
- NaN to json and back [\#515](https://github.com/nlohmann/json/issues/515)
|
||||||
|
- Comparison of NaN [\#514](https://github.com/nlohmann/json/issues/514)
|
||||||
|
- why it's not possible to serialize c++11 enums directly [\#513](https://github.com/nlohmann/json/issues/513)
|
||||||
|
- clang compile error: use of overloaded operator '\<=' is ambiguous with \(nlohmann::json{{"a", 5}}\)\["a"\] \<= 10 [\#512](https://github.com/nlohmann/json/issues/512)
|
||||||
|
- Why not also look inside the type for \(static\) to\_json and from\_json funtions? [\#511](https://github.com/nlohmann/json/issues/511)
|
||||||
|
- Parser issues [\#509](https://github.com/nlohmann/json/issues/509)
|
||||||
|
- I may not understand [\#507](https://github.com/nlohmann/json/issues/507)
|
||||||
|
- VS2017 min / max problem for 2.1.1 [\#506](https://github.com/nlohmann/json/issues/506)
|
||||||
|
- CBOR/MessagePack is not read until the end [\#505](https://github.com/nlohmann/json/issues/505)
|
||||||
|
- Assertion error \(OSS-Fuzz 856\) [\#504](https://github.com/nlohmann/json/issues/504)
|
||||||
|
- Return position in parse error exceptions [\#503](https://github.com/nlohmann/json/issues/503)
|
||||||
|
- conversion from/to C array is not supported [\#502](https://github.com/nlohmann/json/issues/502)
|
||||||
|
- error C2338: could not find to\_json\(\) method in T's namespace [\#501](https://github.com/nlohmann/json/issues/501)
|
||||||
|
- Test suite fails in en\_GB.UTF-8 [\#500](https://github.com/nlohmann/json/issues/500)
|
||||||
|
- cannot use operator\[\] with number [\#499](https://github.com/nlohmann/json/issues/499)
|
||||||
|
- consider using \_\_cpp\_exceptions and/or \_\_EXCEPTIONS to disable/enable exception support [\#498](https://github.com/nlohmann/json/issues/498)
|
||||||
|
- Stack-overflow \(OSS-Fuzz issue 814\) [\#497](https://github.com/nlohmann/json/issues/497)
|
||||||
|
- Using in Unreal Engine - handling custom types conversion [\#495](https://github.com/nlohmann/json/issues/495)
|
||||||
|
- Conversion from vector\<bool\> to json fails to build [\#494](https://github.com/nlohmann/json/issues/494)
|
||||||
|
- fill\_line\_buffer incorrectly tests m\_stream for eof but not fail or bad bits [\#493](https://github.com/nlohmann/json/issues/493)
|
||||||
|
- Compiling with \_GLIBCXX\_DEBUG yields iterator-comparison warnings during tests [\#492](https://github.com/nlohmann/json/issues/492)
|
||||||
|
- crapy interface [\#491](https://github.com/nlohmann/json/issues/491)
|
||||||
|
- Fix Visual Studo 2013 builds. [\#490](https://github.com/nlohmann/json/issues/490)
|
||||||
|
- Failed to compile with -D\_GLIBCXX\_PARALLEL [\#489](https://github.com/nlohmann/json/issues/489)
|
||||||
|
- Input several field with the same name [\#488](https://github.com/nlohmann/json/issues/488)
|
||||||
|
- read in .json file yields strange sizes [\#487](https://github.com/nlohmann/json/issues/487)
|
||||||
|
- json::value\_t can't be a map's key type in VC++ 2015 [\#486](https://github.com/nlohmann/json/issues/486)
|
||||||
|
- Using fifo\_map [\#485](https://github.com/nlohmann/json/issues/485)
|
||||||
|
- Cannot get float pointer for value stored as `0` [\#484](https://github.com/nlohmann/json/issues/484)
|
||||||
|
- byte string support [\#483](https://github.com/nlohmann/json/issues/483)
|
||||||
|
- For a header-only library you have to clone 214MB [\#482](https://github.com/nlohmann/json/issues/482)
|
||||||
|
- https://github.com/nlohmann/json\#execute-unit-tests [\#481](https://github.com/nlohmann/json/issues/481)
|
||||||
|
- Remove deprecated constructor basic\_json\(std::istream&\) [\#480](https://github.com/nlohmann/json/issues/480)
|
||||||
|
- writing the binary json file? [\#479](https://github.com/nlohmann/json/issues/479)
|
||||||
|
- CBOR/MessagePack from uint8\_t \* and size [\#478](https://github.com/nlohmann/json/issues/478)
|
||||||
|
- Streaming binary representations [\#477](https://github.com/nlohmann/json/issues/477)
|
||||||
|
- Reuse memory in to\_cbor and to\_msgpack functions [\#476](https://github.com/nlohmann/json/issues/476)
|
||||||
|
- Error Using JSON Library with arrays C++ [\#475](https://github.com/nlohmann/json/issues/475)
|
||||||
|
- Moving forward to version 3.0.0 [\#474](https://github.com/nlohmann/json/issues/474)
|
||||||
|
- Inconsistent behavior in conversion to array type [\#473](https://github.com/nlohmann/json/issues/473)
|
||||||
|
- Create a \[key:member\_pointer\] map to ease parsing custom types [\#471](https://github.com/nlohmann/json/issues/471)
|
||||||
|
- MSVC 2015 update 2 [\#469](https://github.com/nlohmann/json/issues/469)
|
||||||
|
- VS2017 implicit to std::string conversion fix. [\#464](https://github.com/nlohmann/json/issues/464)
|
||||||
|
- How to make sure a string or string literal is a valid JSON? [\#458](https://github.com/nlohmann/json/issues/458)
|
||||||
|
- basic\_json templated on a "policy" class [\#456](https://github.com/nlohmann/json/issues/456)
|
||||||
|
- json::value\(const json\_pointer&, ValueType\) requires exceptions to return the default value. [\#440](https://github.com/nlohmann/json/issues/440)
|
||||||
|
- is it possible merge two json object [\#428](https://github.com/nlohmann/json/issues/428)
|
||||||
|
- Is it possible to turn this into a shared library? [\#420](https://github.com/nlohmann/json/issues/420)
|
||||||
|
- Further thoughts on performance improvements [\#418](https://github.com/nlohmann/json/issues/418)
|
||||||
|
- nan number stored as null [\#388](https://github.com/nlohmann/json/issues/388)
|
||||||
|
- Behavior of operator\>\> should more closely resemble that of built-in overloads. [\#367](https://github.com/nlohmann/json/issues/367)
|
||||||
|
- Request: range-based-for over a json-object to expose .first/.second [\#350](https://github.com/nlohmann/json/issues/350)
|
||||||
|
- feature wish: JSONPath [\#343](https://github.com/nlohmann/json/issues/343)
|
||||||
|
- UTF-8/Unicode escape and dump [\#330](https://github.com/nlohmann/json/issues/330)
|
||||||
|
- Serialized value not always can be parsed. [\#329](https://github.com/nlohmann/json/issues/329)
|
||||||
|
- Is there a way to forward declare nlohmann::json? [\#314](https://github.com/nlohmann/json/issues/314)
|
||||||
|
- Exception line [\#301](https://github.com/nlohmann/json/issues/301)
|
||||||
|
- Do not throw exception when default\_value's type does not match the actual type [\#278](https://github.com/nlohmann/json/issues/278)
|
||||||
|
- dump\(\) method doesn't work with a custom allocator [\#268](https://github.com/nlohmann/json/issues/268)
|
||||||
|
- Readme documentation enhancements [\#248](https://github.com/nlohmann/json/issues/248)
|
||||||
|
- Use user-defined exceptions [\#244](https://github.com/nlohmann/json/issues/244)
|
||||||
|
- Incorrect C++11 allocator model support [\#161](https://github.com/nlohmann/json/issues/161)
|
||||||
|
|
||||||
|
- :white\_check\_mark: re-added tests for algorithms [\#879](https://github.com/nlohmann/json/pull/879) ([nlohmann](https://github.com/nlohmann))
|
||||||
|
- Overworked library toward 3.0.0 release [\#875](https://github.com/nlohmann/json/pull/875) ([nlohmann](https://github.com/nlohmann))
|
||||||
|
- :rotating\_light: remove C4996 warnings \#872 [\#873](https://github.com/nlohmann/json/pull/873) ([nlohmann](https://github.com/nlohmann))
|
||||||
|
- :boom: throwing an exception in case dump encounters a non-UTF-8 string \#838 [\#870](https://github.com/nlohmann/json/pull/870) ([nlohmann](https://github.com/nlohmann))
|
||||||
|
- :memo: fixing documentation \#867 [\#868](https://github.com/nlohmann/json/pull/868) ([nlohmann](https://github.com/nlohmann))
|
||||||
|
- iter\_impl template conformance with C++17 [\#860](https://github.com/nlohmann/json/pull/860) ([bogemic](https://github.com/bogemic))
|
||||||
|
- Std allocator conformance cpp17 [\#856](https://github.com/nlohmann/json/pull/856) ([bogemic](https://github.com/bogemic))
|
||||||
|
- cmake: use BUILD\_INTERFACE/INSTALL\_INTERFACE [\#855](https://github.com/nlohmann/json/pull/855) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- to/from\_json: add a MSVC-specific static\_assert to force a stacktrace [\#854](https://github.com/nlohmann/json/pull/854) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- Add .natvis for MSVC debug view [\#844](https://github.com/nlohmann/json/pull/844) ([TinyTinni](https://github.com/TinyTinni))
|
||||||
|
- Updated hunter package links [\#829](https://github.com/nlohmann/json/pull/829) ([jowr](https://github.com/jowr))
|
||||||
|
- Typos README [\#811](https://github.com/nlohmann/json/pull/811) ([Itja](https://github.com/Itja))
|
||||||
|
- add forwarding references to json\_ref constructor [\#807](https://github.com/nlohmann/json/pull/807) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- Add transparent comparator and perfect forwarding support to find\(\) and count\(\) [\#795](https://github.com/nlohmann/json/pull/795) ([jseward](https://github.com/jseward))
|
||||||
|
- Error : 'identifier "size\_t" is undefined' in linux [\#793](https://github.com/nlohmann/json/pull/793) ([sonulohani](https://github.com/sonulohani))
|
||||||
|
- Fix Visual Studio 2017 warnings [\#788](https://github.com/nlohmann/json/pull/788) ([jseward](https://github.com/jseward))
|
||||||
|
- Fix warning C4706 on Visual Studio 2017 [\#785](https://github.com/nlohmann/json/pull/785) ([jseward](https://github.com/jseward))
|
||||||
|
- Set GENERATE\_TAGFILE in Doxyfile [\#783](https://github.com/nlohmann/json/pull/783) ([eld00d](https://github.com/eld00d))
|
||||||
|
- using more CMake [\#765](https://github.com/nlohmann/json/pull/765) ([nlohmann](https://github.com/nlohmann))
|
||||||
|
- Simplified istream handing \#367 [\#764](https://github.com/nlohmann/json/pull/764) ([pjkundert](https://github.com/pjkundert))
|
||||||
|
- Add info for the vcpkg package. [\#753](https://github.com/nlohmann/json/pull/753) ([gregmarr](https://github.com/gregmarr))
|
||||||
|
- fix from\_json implementation for pair/tuple [\#708](https://github.com/nlohmann/json/pull/708) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- Update json.hpp [\#686](https://github.com/nlohmann/json/pull/686) ([GoWebProd](https://github.com/GoWebProd))
|
||||||
|
- Remove duplicate word [\#685](https://github.com/nlohmann/json/pull/685) ([daixtrose](https://github.com/daixtrose))
|
||||||
|
- To fix compilation issue for intel OSX compiler [\#682](https://github.com/nlohmann/json/pull/682) ([kbthomp1](https://github.com/kbthomp1))
|
||||||
|
- Digraph warning [\#679](https://github.com/nlohmann/json/pull/679) ([traits](https://github.com/traits))
|
||||||
|
- massage -\> message [\#678](https://github.com/nlohmann/json/pull/678) ([DmitryKuk](https://github.com/DmitryKuk))
|
||||||
|
- Fix "not constraint" grammar in docs [\#674](https://github.com/nlohmann/json/pull/674) ([wincent](https://github.com/wincent))
|
||||||
|
- Add documentation for integration with CMake and hunter [\#671](https://github.com/nlohmann/json/pull/671) ([dan-42](https://github.com/dan-42))
|
||||||
|
- REFACTOR: rewrite CMakeLists.txt for better inlcude and reuse [\#669](https://github.com/nlohmann/json/pull/669) ([dan-42](https://github.com/dan-42))
|
||||||
|
- enable\_testing only if the JSON\_BuildTests is ON [\#666](https://github.com/nlohmann/json/pull/666) ([effolkronium](https://github.com/effolkronium))
|
||||||
|
- Support moving from rvalues in std::initializer\_list [\#663](https://github.com/nlohmann/json/pull/663) ([himikof](https://github.com/himikof))
|
||||||
|
- add ensure\_ascii parameter to dump. \#330 [\#654](https://github.com/nlohmann/json/pull/654) ([ryanjmulder](https://github.com/ryanjmulder))
|
||||||
|
- Rename BuildTests to JSON\_BuildTests [\#652](https://github.com/nlohmann/json/pull/652) ([olegendo](https://github.com/olegendo))
|
||||||
|
- Don't include \<iostream\>, use std::make\_shared [\#650](https://github.com/nlohmann/json/pull/650) ([olegendo](https://github.com/olegendo))
|
||||||
|
- Refacto/split basic json [\#643](https://github.com/nlohmann/json/pull/643) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- fix typo in operator\_\_notequal example [\#630](https://github.com/nlohmann/json/pull/630) ([Chocobo1](https://github.com/Chocobo1))
|
||||||
|
- Fix MSVC warning C4819 [\#629](https://github.com/nlohmann/json/pull/629) ([Chocobo1](https://github.com/Chocobo1))
|
||||||
|
- \[BugFix\] Add parentheses around std::min [\#626](https://github.com/nlohmann/json/pull/626) ([koemeet](https://github.com/koemeet))
|
||||||
|
- add pair/tuple conversions [\#624](https://github.com/nlohmann/json/pull/624) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- remove std::pair support [\#615](https://github.com/nlohmann/json/pull/615) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- Add pair support, fix CompatibleObject conversions \(fixes \#600\) [\#609](https://github.com/nlohmann/json/pull/609) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- \#550 Fix iterator related compiling issues for Intel icc [\#598](https://github.com/nlohmann/json/pull/598) ([HenryRLee](https://github.com/HenryRLee))
|
||||||
|
- Issue \#593 Fix the arithmetic operators in the iterator and reverse iterator [\#595](https://github.com/nlohmann/json/pull/595) ([HenryRLee](https://github.com/HenryRLee))
|
||||||
|
- fix doxygen error of basic\_json::get\(\) [\#583](https://github.com/nlohmann/json/pull/583) ([zhaohuaxishi](https://github.com/zhaohuaxishi))
|
||||||
|
- Fixing assignement for iterator wrapper second, and adding unit test [\#579](https://github.com/nlohmann/json/pull/579) ([Type1J](https://github.com/Type1J))
|
||||||
|
- Adding first and second properties to iteration\_proxy\_internal [\#578](https://github.com/nlohmann/json/pull/578) ([Type1J](https://github.com/Type1J))
|
||||||
|
- Adding support for Meson. [\#576](https://github.com/nlohmann/json/pull/576) ([Type1J](https://github.com/Type1J))
|
||||||
|
- add enum class default conversions [\#545](https://github.com/nlohmann/json/pull/545) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- Properly pop diagnostics [\#540](https://github.com/nlohmann/json/pull/540) ([tinloaf](https://github.com/tinloaf))
|
||||||
|
- Add Visual Studio 17 image to appveyor build matrix [\#536](https://github.com/nlohmann/json/pull/536) ([vpetrigo](https://github.com/vpetrigo))
|
||||||
|
- UTF8 encoding enhancement [\#534](https://github.com/nlohmann/json/pull/534) ([TedLyngmo](https://github.com/TedLyngmo))
|
||||||
|
- Fix typo [\#530](https://github.com/nlohmann/json/pull/530) ([berkus](https://github.com/berkus))
|
||||||
|
- Make exception base class visible in basic\_json [\#526](https://github.com/nlohmann/json/pull/526) ([krzysztofwos](https://github.com/krzysztofwos))
|
||||||
|
- :art: Namespace `uint8\_t` from the C++ stdlib [\#510](https://github.com/nlohmann/json/pull/510) ([alex-weej](https://github.com/alex-weej))
|
||||||
|
- add to\_json method for C arrays [\#508](https://github.com/nlohmann/json/pull/508) ([theodelrieu](https://github.com/theodelrieu))
|
||||||
|
- Fix -Weffc++ warnings \(GNU 6.3.1\) [\#496](https://github.com/nlohmann/json/pull/496) ([TedLyngmo](https://github.com/TedLyngmo))
|
||||||
|
|
||||||
## [v2.1.1](https://github.com/nlohmann/json/releases/tag/v2.1.1) (2017-02-25)
|
## [v2.1.1](https://github.com/nlohmann/json/releases/tag/v2.1.1) (2017-02-25)
|
||||||
[Full Changelog](https://github.com/nlohmann/json/compare/v2.1.0...v2.1.1)
|
[Full Changelog](https://github.com/nlohmann/json/compare/v2.1.0...v2.1.1)
|
||||||
|
|
||||||
|
@ -129,7 +548,6 @@ All notable changes to this project will be documented in this file. This projec
|
||||||
- Performance improvements [\#365](https://github.com/nlohmann/json/issues/365)
|
- Performance improvements [\#365](https://github.com/nlohmann/json/issues/365)
|
||||||
- 'to\_string' is not a member of 'std' [\#364](https://github.com/nlohmann/json/issues/364)
|
- 'to\_string' is not a member of 'std' [\#364](https://github.com/nlohmann/json/issues/364)
|
||||||
- Optional comment support. [\#363](https://github.com/nlohmann/json/issues/363)
|
- Optional comment support. [\#363](https://github.com/nlohmann/json/issues/363)
|
||||||
- Loss of precision when serializing \<double\> [\#360](https://github.com/nlohmann/json/issues/360)
|
|
||||||
- Crash in dump\(\) from a static object [\#359](https://github.com/nlohmann/json/issues/359)
|
- Crash in dump\(\) from a static object [\#359](https://github.com/nlohmann/json/issues/359)
|
||||||
- json::parse\(...\) vs json j; j.parse\(...\) [\#357](https://github.com/nlohmann/json/issues/357)
|
- json::parse\(...\) vs json j; j.parse\(...\) [\#357](https://github.com/nlohmann/json/issues/357)
|
||||||
- Hi, is there any method to dump json to string with the insert order rather than alphabets [\#356](https://github.com/nlohmann/json/issues/356)
|
- Hi, is there any method to dump json to string with the insert order rather than alphabets [\#356](https://github.com/nlohmann/json/issues/356)
|
||||||
|
|
49
Makefile
49
Makefile
|
@ -1,8 +1,38 @@
|
||||||
.PHONY: pretty clean ChangeLog.md
|
.PHONY: pretty clean ChangeLog.md
|
||||||
|
|
||||||
|
SRCS = develop/json.hpp \
|
||||||
|
develop/json_fwd.hpp \
|
||||||
|
develop/detail/macro_scope.hpp \
|
||||||
|
develop/detail/macro_unscope.hpp \
|
||||||
|
develop/detail/meta.hpp \
|
||||||
|
develop/detail/exceptions.hpp \
|
||||||
|
develop/detail/value_t.hpp \
|
||||||
|
develop/detail/conversions/from_json.hpp \
|
||||||
|
develop/detail/conversions/to_json.hpp \
|
||||||
|
develop/detail/parsing/input_adapters.hpp \
|
||||||
|
develop/detail/parsing/lexer.hpp \
|
||||||
|
develop/detail/parsing/parser.hpp \
|
||||||
|
develop/detail/iterators/primitive_iterator.hpp \
|
||||||
|
develop/detail/iterators/internal_iterator.hpp \
|
||||||
|
develop/detail/iterators/iter_impl.hpp \
|
||||||
|
develop/detail/iterators/iteration_proxy.hpp \
|
||||||
|
develop/detail/iterators/json_reverse_iterator.hpp \
|
||||||
|
develop/detail/parsing/output_adapters.hpp \
|
||||||
|
develop/detail/parsing/binary_reader.hpp \
|
||||||
|
develop/detail/parsing/binary_writer.hpp \
|
||||||
|
develop/detail/serializer.hpp \
|
||||||
|
develop/detail/json_ref.hpp \
|
||||||
|
develop/adl_serializer.hpp
|
||||||
|
|
||||||
|
UNAME = $(shell uname)
|
||||||
|
CXX=clang++
|
||||||
|
|
||||||
|
# main target
|
||||||
all:
|
all:
|
||||||
|
@echo "amalgamate - amalgamate file src/json.hpp from the develop sources"
|
||||||
@echo "ChangeLog.md - generate ChangeLog file"
|
@echo "ChangeLog.md - generate ChangeLog file"
|
||||||
@echo "check - compile and execute test suite"
|
@echo "check - compile and execute test suite"
|
||||||
|
@echo "check-amalagamation - check whether sources have been amalgamated"
|
||||||
@echo "check-fast - compile and execute test suite (skip long-running tests)"
|
@echo "check-fast - compile and execute test suite (skip long-running tests)"
|
||||||
@echo "clean - remove built files"
|
@echo "clean - remove built files"
|
||||||
@echo "coverage - create coverage information with lcov"
|
@echo "coverage - create coverage information with lcov"
|
||||||
|
@ -16,7 +46,6 @@ all:
|
||||||
@echo "pedantic_gcc - run GCC with maximal warning flags"
|
@echo "pedantic_gcc - run GCC with maximal warning flags"
|
||||||
@echo "pretty - beautify code with Artistic Style"
|
@echo "pretty - beautify code with Artistic Style"
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# unit tests
|
# unit tests
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -95,7 +124,7 @@ pedantic_clang:
|
||||||
|
|
||||||
# calling GCC with most warnings
|
# calling GCC with most warnings
|
||||||
pedantic_gcc:
|
pedantic_gcc:
|
||||||
$(MAKE) json_unit CXX=g++ CXXFLAGS="\
|
$(MAKE) json_unit CXXFLAGS="\
|
||||||
-std=c++11 \
|
-std=c++11 \
|
||||||
-Wno-deprecated-declarations \
|
-Wno-deprecated-declarations \
|
||||||
-Werror \
|
-Werror \
|
||||||
|
@ -218,9 +247,23 @@ pretty:
|
||||||
--indent-col1-comments --pad-oper --pad-header --align-pointer=type \
|
--indent-col1-comments --pad-oper --pad-header --align-pointer=type \
|
||||||
--align-reference=type --add-brackets --convert-tabs --close-templates \
|
--align-reference=type --add-brackets --convert-tabs --close-templates \
|
||||||
--lineend=linux --preserve-date --suffix=none --formatted \
|
--lineend=linux --preserve-date --suffix=none --formatted \
|
||||||
src/json.hpp test/src/*.cpp \
|
$(SRCS) src/json.hpp test/src/*.cpp \
|
||||||
benchmarks/src/benchmarks.cpp doc/examples/*.cpp
|
benchmarks/src/benchmarks.cpp doc/examples/*.cpp
|
||||||
|
|
||||||
|
# create single header file
|
||||||
|
amalgamate: src/json.hpp
|
||||||
|
|
||||||
|
src/json.hpp: $(SRCS)
|
||||||
|
develop/amalgamate/amalgamate.py -c develop/amalgamate/config.json -s develop --verbose=yes
|
||||||
|
$(MAKE) pretty
|
||||||
|
|
||||||
|
# check if src/json.hpp has been amalgamated from the develop sources
|
||||||
|
check-amalagamation:
|
||||||
|
@mv src/json.hpp src/json.hpp~
|
||||||
|
@$(MAKE) amalgamate
|
||||||
|
@diff src/json.hpp src/json.hpp~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv src/json.hpp~ src/json.hpp ; false)
|
||||||
|
@mv src/json.hpp~ src/json.hpp
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# changelog
|
# changelog
|
||||||
|
|
36
README.md
36
README.md
|
@ -5,7 +5,7 @@
|
||||||
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
|
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
|
||||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
|
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
|
||||||
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade)
|
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade)
|
||||||
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/yCTCvQi0ZVxGuL3L)
|
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
|
||||||
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
|
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
|
||||||
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
|
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
|
||||||
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
|
[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
|
||||||
|
@ -75,12 +75,10 @@ If you are using [hunter](https://github.com/ruslo/hunter/) on your project for
|
||||||
|
|
||||||
If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json). Please see the vcpkg project for any issues regarding the packaging.
|
If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json). Please see the vcpkg project for any issues regarding the packaging.
|
||||||
|
|
||||||
:warning: [Version 3.0.0](https://github.com/nlohmann/json/wiki/Road-toward-3.0.0) is currently under development. Branch `develop` is used for the ongoing work and is probably **unstable**. Please use the `master` branch for the last stable version 2.1.1.
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a602f275f0359ab181221384989810604.html#a602f275f0359ab181221384989810604)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)).
|
Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5338e282d1d02bed389d852dd670d98d.html#a5338e282d1d02bed389d852dd670d98d)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)).
|
||||||
|
|
||||||
### JSON as first-class data type
|
### JSON as first-class data type
|
||||||
|
|
||||||
|
@ -739,13 +737,13 @@ json j_from_msgpack = json::from_msgpack(v_msgpack);
|
||||||
|
|
||||||
## Supported compilers
|
## Supported compilers
|
||||||
|
|
||||||
Though it's 2016 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
|
Though it's 2017 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
|
||||||
|
|
||||||
- GCC 4.9 - 7.1 (and possibly later)
|
- GCC 4.9 - 7.2 (and possibly later)
|
||||||
- Clang 3.4 - 5.0 (and possibly later)
|
- Clang 3.4 - 5.0 (and possibly later)
|
||||||
- Intel C++ Compiler 17.0.2 (and possibly later)
|
- Intel C++ Compiler 17.0.2 (and possibly later)
|
||||||
- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
|
- Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
|
||||||
- Microsoft Visual C++ 2017 / Build Tools 15.1.548.43366 (and possibly later)
|
- Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later)
|
||||||
|
|
||||||
I would be happy to learn about other compilers/versions.
|
I would be happy to learn about other compilers/versions.
|
||||||
|
|
||||||
|
@ -785,9 +783,11 @@ The following compilers are currently used in continuous integration at [Travis]
|
||||||
| Clang Xcode 8.1 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
|
| Clang Xcode 8.1 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
|
||||||
| Clang Xcode 8.2 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
|
| Clang Xcode 8.2 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
|
||||||
| Clang Xcode 8.3 | Darwin Kernel Version 16.5.0 (macOS 10.12.4) | Apple LLVM version 8.1.0 (clang-802.0.38) |
|
| Clang Xcode 8.3 | Darwin Kernel Version 16.5.0 (macOS 10.12.4) | Apple LLVM version 8.1.0 (clang-802.0.38) |
|
||||||
| Clang Xcode 9 beta | Darwin Kernel Version 16.6.0 (macOS 10.12.5) | Apple LLVM version 9.0.0 (clang-900.0.26) |
|
| Clang Xcode 9.0 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.37) |
|
||||||
| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1 |
|
| Clang Xcode 9.1 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.38) |
|
||||||
| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.1.1012.6693 |
|
| Clang Xcode 9.2 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 8.1.0 (clang-900.0.39.2) |
|
||||||
|
| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1, MSVC 19.0.24215.1 |
|
||||||
|
| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.5.180.51428, MSVC 19.12.25830.2 |
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -805,19 +805,21 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
||||||
|
|
||||||
* * *
|
* * *
|
||||||
|
|
||||||
The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Bjoern Hoehrmann](http://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
|
The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
If you have questions regarding the library, I would like to invite you to [open an issue at Github](https://github.com/nlohmann/json/issues/new). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at Github allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases.
|
If you have questions regarding the library, I would like to invite you to [open an issue at Github](https://github.com/nlohmann/json/issues/new). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at Github allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases.
|
||||||
|
|
||||||
Only if your request would contain confidential information, please [send me an email](mailto:mail@nlohmann.me).
|
Only if your request would contain confidential information, please [send me an email](mailto:mail@nlohmann.me). For encrypted messages, please use [this key](https://keybase.io/nlohmann/pgp_keys.asc).
|
||||||
|
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
I deeply appreciate the help of the following people.
|
I deeply appreciate the help of the following people.
|
||||||
|
|
||||||
|
![Contributors](https://raw.githubusercontent.com/nlohmann/json/develop/doc/avatars.png)
|
||||||
|
|
||||||
- [Teemperor](https://github.com/Teemperor) implemented CMake support and lcov integration, realized escape and Unicode handling in the string parser, and fixed the JSON serialization.
|
- [Teemperor](https://github.com/Teemperor) implemented CMake support and lcov integration, realized escape and Unicode handling in the string parser, and fixed the JSON serialization.
|
||||||
- [elliotgoodrich](https://github.com/elliotgoodrich) fixed an issue with double deletion in the iterator classes.
|
- [elliotgoodrich](https://github.com/elliotgoodrich) fixed an issue with double deletion in the iterator classes.
|
||||||
- [kirkshoop](https://github.com/kirkshoop) made the iterators of the class composable to other libraries.
|
- [kirkshoop](https://github.com/kirkshoop) made the iterators of the class composable to other libraries.
|
||||||
|
@ -910,6 +912,10 @@ I deeply appreciate the help of the following people.
|
||||||
- [Pavel](https://github.com/crea7or) helped fixing some warnings in MSVC.
|
- [Pavel](https://github.com/crea7or) helped fixing some warnings in MSVC.
|
||||||
- [Jamie Seward](https://github.com/jseward) avoided unneccessary string copies in `find()` and `count()`.
|
- [Jamie Seward](https://github.com/jseward) avoided unneccessary string copies in `find()` and `count()`.
|
||||||
- [Mitja](https://github.com/Itja) fixed some typos.
|
- [Mitja](https://github.com/Itja) fixed some typos.
|
||||||
|
- [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links.
|
||||||
|
- [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view.
|
||||||
|
- [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings.
|
||||||
|
- [Eren Okka](https://github.com/erengy) fixed some MSVC warnings.
|
||||||
|
|
||||||
|
|
||||||
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
|
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
|
||||||
|
@ -940,7 +946,7 @@ The library itself contains of a single header file licensed under the MIT licen
|
||||||
- [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox)
|
- [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox)
|
||||||
- [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS
|
- [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS
|
||||||
- [**Valgrind**](http://valgrind.org) to check for correct memory management
|
- [**Valgrind**](http://valgrind.org) to check for correct memory management
|
||||||
- [**Wandbox**](http://melpon.org/wandbox) for [online examples](https://wandbox.org/permlink/yCTCvQi0ZVxGuL3L)
|
- [**Wandbox**](http://melpon.org/wandbox) for [online examples](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
|
||||||
|
|
||||||
|
|
||||||
## Projects using JSON for Modern C++
|
## Projects using JSON for Modern C++
|
||||||
|
@ -954,7 +960,7 @@ The library is currently used in Apple macOS Sierra and iOS 10. I am not sure wh
|
||||||
- As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
|
- As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
|
||||||
- The library supports **Unicode input** as follows:
|
- The library supports **Unicode input** as follows:
|
||||||
- Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 7159](http://rfc7159.net/rfc7159#rfc.section.8.1).
|
- Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 7159](http://rfc7159.net/rfc7159#rfc.section.8.1).
|
||||||
- Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse errors.
|
- Other encodings such as Latin-1, UTF-16, or UTF-32 are not supported and will yield parse or serialization errors.
|
||||||
- [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
|
- [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1) will not be replaced by the library.
|
||||||
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
||||||
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
- The strings stored in the library are UTF-8 encoded. When using the default string type (`std::string`), note that its length/size functions return the number of stored bytes rather than the number of characters or glyphs.
|
||||||
|
@ -972,7 +978,7 @@ $ mkdir build
|
||||||
$ cd build
|
$ cd build
|
||||||
$ cmake ..
|
$ cmake ..
|
||||||
$ cmake --build .
|
$ cmake --build .
|
||||||
$ ctest
|
$ ctest --output-on-failure
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
|
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
|
||||||
|
|
45
develop/adl_serializer.hpp
Normal file
45
develop/adl_serializer.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "detail/conversions/from_json.hpp"
|
||||||
|
#include "detail/conversions/to_json.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
template<typename, typename>
|
||||||
|
struct adl_serializer
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
@brief convert a JSON value to any value type
|
||||||
|
|
||||||
|
This function is usually called by the `get()` function of the
|
||||||
|
@ref basic_json class (either explicit or via conversion operators).
|
||||||
|
|
||||||
|
@param[in] j JSON value to read from
|
||||||
|
@param[in,out] val value to write to
|
||||||
|
*/
|
||||||
|
template<typename BasicJsonType, typename ValueType>
|
||||||
|
static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
|
||||||
|
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
|
||||||
|
{
|
||||||
|
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief convert any value type to a JSON value
|
||||||
|
|
||||||
|
This function is usually called by the constructors of the @ref basic_json
|
||||||
|
class.
|
||||||
|
|
||||||
|
@param[in,out] j JSON value to write to
|
||||||
|
@param[in] val value to read from
|
||||||
|
*/
|
||||||
|
template<typename BasicJsonType, typename ValueType>
|
||||||
|
static void to_json(BasicJsonType& j, ValueType&& val) noexcept(
|
||||||
|
noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))
|
||||||
|
{
|
||||||
|
::nlohmann::to_json(j, std::forward<ValueType>(val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
10
develop/amalgamate/CHANGES.md
Normal file
10
develop/amalgamate/CHANGES.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
The following changes have been made to the code with respect to <https://github.com/edlund/amalgamate/commit/c91f07eea1133aa184f652b8f1398eaf03586208>:
|
||||||
|
|
||||||
|
- Resolved inspection results from PyCharm:
|
||||||
|
- replaced tabs with spaces
|
||||||
|
- added encoding annotation
|
||||||
|
- reindented file to remove trailing whitespaces
|
||||||
|
- unused import `sys`
|
||||||
|
- membership check
|
||||||
|
- made function from `_is_within`
|
||||||
|
- removed unused variable `actual_path`
|
27
develop/amalgamate/LICENSE.md
Normal file
27
develop/amalgamate/LICENSE.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
amalgamate.py - Amalgamate C source and header files
|
||||||
|
Copyright (c) 2012, Erik Edlund <erik.edlund@32767.se>
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of Erik Edlund, nor the names of its contributors may
|
||||||
|
be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
66
develop/amalgamate/README.md
Normal file
66
develop/amalgamate/README.md
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
# amalgamate.py - Amalgamate C source and header files
|
||||||
|
|
||||||
|
Origin: https://bitbucket.org/erikedlund/amalgamate
|
||||||
|
|
||||||
|
Mirror: https://github.com/edlund/amalgamate
|
||||||
|
|
||||||
|
`amalgamate.py` aims to make it easy to use SQLite-style C source and header
|
||||||
|
amalgamation in projects.
|
||||||
|
|
||||||
|
For more information, please refer to: http://sqlite.org/amalgamation.html
|
||||||
|
|
||||||
|
## Here be dragons
|
||||||
|
|
||||||
|
`amalgamate.py` is quite dumb, it only knows the bare minimum about C code
|
||||||
|
required in order to be able to handle trivial include directives. It can
|
||||||
|
produce weird results for unexpected code.
|
||||||
|
|
||||||
|
Things to be aware of:
|
||||||
|
|
||||||
|
`amalgamate.py` will not handle complex include directives correctly:
|
||||||
|
|
||||||
|
#define HEADER_PATH "path/to/header.h"
|
||||||
|
#include HEADER_PATH
|
||||||
|
|
||||||
|
In the above example, `path/to/header.h` will not be included in the
|
||||||
|
amalgamation (HEADER_PATH is never expanded).
|
||||||
|
|
||||||
|
`amalgamate.py` makes the assumption that each source and header file which
|
||||||
|
is not empty will end in a new-line character, which is not immediately
|
||||||
|
preceded by a backslash character (see 5.1.1.2p1.2 of ISO C99).
|
||||||
|
|
||||||
|
`amalgamate.py` should be usable with C++ code, but raw string literals from
|
||||||
|
C++11 will definitely cause problems:
|
||||||
|
|
||||||
|
R"delimiter(Terrible raw \ data " #include <sneaky.hpp>)delimiter"
|
||||||
|
R"delimiter(Terrible raw \ data " escaping)delimiter"
|
||||||
|
|
||||||
|
In the examples above, `amalgamate.py` will stop parsing the raw string literal
|
||||||
|
when it encounters the first quotation mark, which will produce unexpected
|
||||||
|
results.
|
||||||
|
|
||||||
|
## Installing amalgamate.py
|
||||||
|
|
||||||
|
Python v.2.7.0 or higher is required.
|
||||||
|
|
||||||
|
`amalgamate.py` can be tested and installed using the following commands:
|
||||||
|
|
||||||
|
./test.sh && sudo -k cp ./amalgamate.py /usr/local/bin/
|
||||||
|
|
||||||
|
## Using amalgamate.py
|
||||||
|
|
||||||
|
amalgamate.py [-v] -c path/to/config.json -s path/to/source/dir \
|
||||||
|
[-p path/to/prologue.(c|h)]
|
||||||
|
|
||||||
|
* The `-c, --config` option should specify the path to a JSON config file which
|
||||||
|
lists the source files, include paths and where to write the resulting
|
||||||
|
amalgamation. Have a look at `test/source.c.json` and `test/include.h.json`
|
||||||
|
to see two examples.
|
||||||
|
|
||||||
|
* The `-s, --source` option should specify the path to the source directory.
|
||||||
|
This is useful for supporting separate source and build directories.
|
||||||
|
|
||||||
|
* The `-p, --prologue` option should specify the path to a file which will be
|
||||||
|
added to the beginning of the amalgamation. It is optional.
|
||||||
|
|
299
develop/amalgamate/amalgamate.py
Executable file
299
develop/amalgamate/amalgamate.py
Executable file
|
@ -0,0 +1,299 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
# amalgamate.py - Amalgamate C source and header files.
|
||||||
|
# Copyright (c) 2012, Erik Edlund <erik.edlund@32767.se>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
# are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# * Neither the name of Erik Edlund, nor the names of its contributors may
|
||||||
|
# be used to endorse or promote products derived from this software without
|
||||||
|
# specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class Amalgamation(object):
|
||||||
|
|
||||||
|
# Prepends self.source_path to file_path if needed.
|
||||||
|
def actual_path(self, file_path):
|
||||||
|
if not os.path.isabs(file_path):
|
||||||
|
file_path = os.path.join(self.source_path, file_path)
|
||||||
|
return file_path
|
||||||
|
|
||||||
|
# Search included file_path in self.include_paths and
|
||||||
|
# in source_dir if specified.
|
||||||
|
def find_included_file(self, file_path, source_dir):
|
||||||
|
search_dirs = self.include_paths[:]
|
||||||
|
if source_dir:
|
||||||
|
search_dirs.insert(0, source_dir)
|
||||||
|
|
||||||
|
for search_dir in search_dirs:
|
||||||
|
search_path = os.path.join(search_dir, file_path)
|
||||||
|
if os.path.isfile(self.actual_path(search_path)):
|
||||||
|
return search_path
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
with open(args.config, 'r') as f:
|
||||||
|
config = json.loads(f.read())
|
||||||
|
for key in config:
|
||||||
|
setattr(self, key, config[key])
|
||||||
|
|
||||||
|
self.verbose = args.verbose == "yes"
|
||||||
|
self.prologue = args.prologue
|
||||||
|
self.source_path = args.source_path
|
||||||
|
self.included_files = []
|
||||||
|
|
||||||
|
# Generate the amalgamation and write it to the target file.
|
||||||
|
def generate(self):
|
||||||
|
amalgamation = ""
|
||||||
|
|
||||||
|
if self.prologue:
|
||||||
|
with open(self.prologue, 'r') as f:
|
||||||
|
amalgamation += datetime.datetime.now().strftime(f.read())
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print("Config:")
|
||||||
|
print(" target = {0}".format(self.target))
|
||||||
|
print(" working_dir = {0}".format(os.getcwd()))
|
||||||
|
print(" include_paths = {0}".format(self.include_paths))
|
||||||
|
print("Creating amalgamation:")
|
||||||
|
for file_path in self.sources:
|
||||||
|
# Do not check the include paths while processing the source
|
||||||
|
# list, all given source paths must be correct.
|
||||||
|
# actual_path = self.actual_path(file_path)
|
||||||
|
print(" - processing \"{0}\"".format(file_path))
|
||||||
|
t = TranslationUnit(file_path, self, True)
|
||||||
|
amalgamation += t.content
|
||||||
|
|
||||||
|
with open(self.target, 'w') as f:
|
||||||
|
f.write(amalgamation)
|
||||||
|
|
||||||
|
print("...done!\n")
|
||||||
|
if self.verbose:
|
||||||
|
print("Files processed: {0}".format(self.sources))
|
||||||
|
print("Files included: {0}".format(self.included_files))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def _is_within(match, matches):
|
||||||
|
for m in matches:
|
||||||
|
if match.start() > m.start() and \
|
||||||
|
match.end() < m.end():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class TranslationUnit(object):
|
||||||
|
# // C++ comment.
|
||||||
|
cpp_comment_pattern = re.compile(r"//.*?\n")
|
||||||
|
|
||||||
|
# /* C comment. */
|
||||||
|
c_comment_pattern = re.compile(r"/\*.*?\*/", re.S)
|
||||||
|
|
||||||
|
# "complex \"stri\\\ng\" value".
|
||||||
|
string_pattern = re.compile("[^']" r'".*?(?<=[^\\])"', re.S)
|
||||||
|
|
||||||
|
# Handle simple include directives. Support for advanced
|
||||||
|
# directives where macros and defines needs to expanded is
|
||||||
|
# not a concern right now.
|
||||||
|
include_pattern = re.compile(
|
||||||
|
r'#\s*include\s+(<|")(?P<path>.*?)("|>)', re.S)
|
||||||
|
|
||||||
|
# #pragma once
|
||||||
|
pragma_once_pattern = re.compile(r'#\s*pragma\s+once', re.S)
|
||||||
|
|
||||||
|
# Search for pattern in self.content, add the match to
|
||||||
|
# contexts if found and update the index accordingly.
|
||||||
|
def _search_content(self, index, pattern, contexts):
|
||||||
|
match = pattern.search(self.content, index)
|
||||||
|
if match:
|
||||||
|
contexts.append(match)
|
||||||
|
return match.end()
|
||||||
|
return index + 2
|
||||||
|
|
||||||
|
# Return all the skippable contexts, i.e., comments and strings
|
||||||
|
def _find_skippable_contexts(self):
|
||||||
|
# Find contexts in the content in which a found include
|
||||||
|
# directive should not be processed.
|
||||||
|
skippable_contexts = []
|
||||||
|
|
||||||
|
# Walk through the content char by char, and try to grab
|
||||||
|
# skippable contexts using regular expressions when found.
|
||||||
|
i = 1
|
||||||
|
content_len = len(self.content)
|
||||||
|
while i < content_len:
|
||||||
|
j = i - 1
|
||||||
|
current = self.content[i]
|
||||||
|
previous = self.content[j]
|
||||||
|
|
||||||
|
if current == '"':
|
||||||
|
# String value.
|
||||||
|
i = self._search_content(j, self.string_pattern,
|
||||||
|
skippable_contexts)
|
||||||
|
elif current == '*' and previous == '/':
|
||||||
|
# C style comment.
|
||||||
|
i = self._search_content(j, self.c_comment_pattern,
|
||||||
|
skippable_contexts)
|
||||||
|
elif current == '/' and previous == '/':
|
||||||
|
# C++ style comment.
|
||||||
|
i = self._search_content(j, self.cpp_comment_pattern,
|
||||||
|
skippable_contexts)
|
||||||
|
else:
|
||||||
|
# Skip to the next char.
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return skippable_contexts
|
||||||
|
|
||||||
|
# Returns True if the match is within list of other matches
|
||||||
|
|
||||||
|
# Removes pragma once from content
|
||||||
|
def _process_pragma_once(self):
|
||||||
|
content_len = len(self.content)
|
||||||
|
if content_len < len("#include <x>"):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Find contexts in the content in which a found include
|
||||||
|
# directive should not be processed.
|
||||||
|
skippable_contexts = self._find_skippable_contexts()
|
||||||
|
|
||||||
|
pragmas = []
|
||||||
|
pragma_once_match = self.pragma_once_pattern.search(self.content)
|
||||||
|
while pragma_once_match:
|
||||||
|
if not _is_within(pragma_once_match, skippable_contexts):
|
||||||
|
pragmas.append(pragma_once_match)
|
||||||
|
|
||||||
|
pragma_once_match = self.pragma_once_pattern.search(self.content,
|
||||||
|
pragma_once_match.end())
|
||||||
|
|
||||||
|
# Handle all collected pragma once directives.
|
||||||
|
prev_end = 0
|
||||||
|
tmp_content = ''
|
||||||
|
for pragma_match in pragmas:
|
||||||
|
tmp_content += self.content[prev_end:pragma_match.start()]
|
||||||
|
prev_end = pragma_match.end()
|
||||||
|
tmp_content += self.content[prev_end:]
|
||||||
|
self.content = tmp_content
|
||||||
|
|
||||||
|
# Include all trivial #include directives into self.content.
|
||||||
|
def _process_includes(self):
|
||||||
|
content_len = len(self.content)
|
||||||
|
if content_len < len("#include <x>"):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Find contexts in the content in which a found include
|
||||||
|
# directive should not be processed.
|
||||||
|
skippable_contexts = self._find_skippable_contexts()
|
||||||
|
|
||||||
|
# Search for include directives in the content, collect those
|
||||||
|
# which should be included into the content.
|
||||||
|
includes = []
|
||||||
|
include_match = self.include_pattern.search(self.content)
|
||||||
|
while include_match:
|
||||||
|
if not _is_within(include_match, skippable_contexts):
|
||||||
|
include_path = include_match.group("path")
|
||||||
|
search_same_dir = include_match.group(1) == '"'
|
||||||
|
found_included_path = self.amalgamation.find_included_file(
|
||||||
|
include_path, self.file_dir if search_same_dir else None)
|
||||||
|
if found_included_path:
|
||||||
|
includes.append((include_match, found_included_path))
|
||||||
|
|
||||||
|
include_match = self.include_pattern.search(self.content,
|
||||||
|
include_match.end())
|
||||||
|
|
||||||
|
# Handle all collected include directives.
|
||||||
|
prev_end = 0
|
||||||
|
tmp_content = ''
|
||||||
|
for include in includes:
|
||||||
|
include_match, found_included_path = include
|
||||||
|
tmp_content += self.content[prev_end:include_match.start()]
|
||||||
|
tmp_content += "// {0}\n".format(include_match.group(0))
|
||||||
|
if found_included_path not in self.amalgamation.included_files:
|
||||||
|
t = TranslationUnit(found_included_path, self.amalgamation, False)
|
||||||
|
tmp_content += t.content
|
||||||
|
prev_end = include_match.end()
|
||||||
|
tmp_content += self.content[prev_end:]
|
||||||
|
self.content = tmp_content
|
||||||
|
|
||||||
|
return len(includes)
|
||||||
|
|
||||||
|
# Make all content processing
|
||||||
|
def _process(self):
|
||||||
|
if not self.is_root:
|
||||||
|
self._process_pragma_once()
|
||||||
|
self._process_includes()
|
||||||
|
|
||||||
|
def __init__(self, file_path, amalgamation, is_root):
|
||||||
|
self.file_path = file_path
|
||||||
|
self.file_dir = os.path.dirname(file_path)
|
||||||
|
self.amalgamation = amalgamation
|
||||||
|
self.is_root = is_root
|
||||||
|
|
||||||
|
self.amalgamation.included_files.append(self.file_path)
|
||||||
|
|
||||||
|
actual_path = self.amalgamation.actual_path(file_path)
|
||||||
|
if not os.path.isfile(actual_path):
|
||||||
|
raise IOError("File not found: \"{0}\"".format(file_path))
|
||||||
|
with open(actual_path, 'r') as f:
|
||||||
|
self.content = f.read()
|
||||||
|
self._process()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
description = "Amalgamate C source and header files."
|
||||||
|
usage = " ".join([
|
||||||
|
"amalgamate.py",
|
||||||
|
"[-v]",
|
||||||
|
"-c path/to/config.json",
|
||||||
|
"-s path/to/source/dir",
|
||||||
|
"[-p path/to/prologue.(c|h)]"
|
||||||
|
])
|
||||||
|
argsparser = argparse.ArgumentParser(
|
||||||
|
description=description, usage=usage)
|
||||||
|
|
||||||
|
argsparser.add_argument("-v", "--verbose", dest="verbose",
|
||||||
|
choices=["yes", "no"], metavar="", help="be verbose")
|
||||||
|
|
||||||
|
argsparser.add_argument("-c", "--config", dest="config",
|
||||||
|
required=True, metavar="", help="path to a JSON config file")
|
||||||
|
|
||||||
|
argsparser.add_argument("-s", "--source", dest="source_path",
|
||||||
|
required=True, metavar="", help="source code path")
|
||||||
|
|
||||||
|
argsparser.add_argument("-p", "--prologue", dest="prologue",
|
||||||
|
required=False, metavar="", help="path to a C prologue file")
|
||||||
|
|
||||||
|
amalgamation = Amalgamation(argsparser.parse_args())
|
||||||
|
amalgamation.generate()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
8
develop/amalgamate/config.json
Normal file
8
develop/amalgamate/config.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"project": "JSON for Modern C++",
|
||||||
|
"target": "src/json.hpp",
|
||||||
|
"sources": [
|
||||||
|
"json.hpp"
|
||||||
|
],
|
||||||
|
"include_paths": ["."]
|
||||||
|
}
|
315
develop/detail/conversions/from_json.hpp
Normal file
315
develop/detail/conversions/from_json.hpp
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // transform
|
||||||
|
#include <array> // array
|
||||||
|
#include <ciso646> // and, not
|
||||||
|
#include <forward_list> // forward_list
|
||||||
|
#include <iterator> // inserter, front_inserter, end
|
||||||
|
#include <string> // string
|
||||||
|
#include <tuple> // tuple, make_tuple
|
||||||
|
#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
|
||||||
|
#include <utility> // pair, declval
|
||||||
|
#include <valarray> // valarray
|
||||||
|
|
||||||
|
#include "detail/exceptions.hpp"
|
||||||
|
#include "detail/macro_scope.hpp"
|
||||||
|
#include "detail/meta.hpp"
|
||||||
|
#include "detail/value_t.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
// overloads for basic_json template parameters
|
||||||
|
template<typename BasicJsonType, typename ArithmeticType,
|
||||||
|
enable_if_t<std::is_arithmetic<ArithmeticType>::value and
|
||||||
|
not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||||
|
int> = 0>
|
||||||
|
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
||||||
|
{
|
||||||
|
switch (static_cast<value_t>(j))
|
||||||
|
{
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
{
|
||||||
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case value_t::number_integer:
|
||||||
|
{
|
||||||
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case value_t::number_float:
|
||||||
|
{
|
||||||
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(not j.is_boolean()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(not j.is_string()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
|
||||||
|
{
|
||||||
|
get_arithmetic_value(j, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
|
||||||
|
{
|
||||||
|
get_arithmetic_value(j, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
|
||||||
|
{
|
||||||
|
get_arithmetic_value(j, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename EnumType,
|
||||||
|
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
||||||
|
void from_json(const BasicJsonType& j, EnumType& e)
|
||||||
|
{
|
||||||
|
typename std::underlying_type<EnumType>::type val;
|
||||||
|
get_arithmetic_value(j, val);
|
||||||
|
e = static_cast<EnumType>(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void from_json(const BasicJsonType& j, typename BasicJsonType::array_t& arr)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(not j.is_array()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward_list doesn't have an insert method
|
||||||
|
template<typename BasicJsonType, typename T, typename Allocator,
|
||||||
|
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
|
||||||
|
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(not j.is_array()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
std::transform(j.rbegin(), j.rend(),
|
||||||
|
std::front_inserter(l), [](const BasicJsonType & i)
|
||||||
|
{
|
||||||
|
return i.template get<T>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// valarray doesn't have an insert method
|
||||||
|
template<typename BasicJsonType, typename T,
|
||||||
|
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>
|
||||||
|
void from_json(const BasicJsonType& j, std::valarray<T>& l)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(not j.is_array()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
l.resize(j.size());
|
||||||
|
std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||||
|
void from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<0> /*unused*/)
|
||||||
|
{
|
||||||
|
using std::end;
|
||||||
|
|
||||||
|
std::transform(j.begin(), j.end(),
|
||||||
|
std::inserter(arr, end(arr)), [](const BasicJsonType & i)
|
||||||
|
{
|
||||||
|
// get<BasicJsonType>() returns *this, this won't call a from_json
|
||||||
|
// method when value_type is BasicJsonType
|
||||||
|
return i.template get<typename CompatibleArrayType::value_type>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||||
|
auto from_json_array_impl(const BasicJsonType& j, CompatibleArrayType& arr, priority_tag<1> /*unused*/)
|
||||||
|
-> decltype(
|
||||||
|
arr.reserve(std::declval<typename CompatibleArrayType::size_type>()),
|
||||||
|
void())
|
||||||
|
{
|
||||||
|
using std::end;
|
||||||
|
|
||||||
|
arr.reserve(j.size());
|
||||||
|
std::transform(j.begin(), j.end(),
|
||||||
|
std::inserter(arr, end(arr)), [](const BasicJsonType & i)
|
||||||
|
{
|
||||||
|
// get<BasicJsonType>() returns *this, this won't call a from_json
|
||||||
|
// method when value_type is BasicJsonType
|
||||||
|
return i.template get<typename CompatibleArrayType::value_type>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T, std::size_t N>
|
||||||
|
void from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, priority_tag<2> /*unused*/)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < N; ++i)
|
||||||
|
{
|
||||||
|
arr[i] = j.at(i).template get<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||||
|
enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value and
|
||||||
|
std::is_convertible<BasicJsonType, typename CompatibleArrayType::value_type>::value and
|
||||||
|
not std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value, int> = 0>
|
||||||
|
void from_json(const BasicJsonType& j, CompatibleArrayType& arr)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(not j.is_array()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
|
||||||
|
from_json_array_impl(j, arr, priority_tag<2> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||||
|
enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
||||||
|
void from_json(const BasicJsonType& j, CompatibleObjectType& obj)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(not j.is_object()))
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
||||||
|
using value_type = typename CompatibleObjectType::value_type;
|
||||||
|
std::transform(
|
||||||
|
inner_object->begin(), inner_object->end(),
|
||||||
|
std::inserter(obj, obj.begin()),
|
||||||
|
[](typename BasicJsonType::object_t::value_type const & p)
|
||||||
|
{
|
||||||
|
return value_type(p.first, p.second.template get<typename CompatibleObjectType::mapped_type>());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// overload for arithmetic types, not chosen for basic_json template arguments
|
||||||
|
// (BooleanType, etc..); note: Is it really necessary to provide explicit
|
||||||
|
// overloads for boolean_t etc. in case of a custom BooleanType which is not
|
||||||
|
// an arithmetic type?
|
||||||
|
template<typename BasicJsonType, typename ArithmeticType,
|
||||||
|
enable_if_t <
|
||||||
|
std::is_arithmetic<ArithmeticType>::value and
|
||||||
|
not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and
|
||||||
|
not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and
|
||||||
|
not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and
|
||||||
|
not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
||||||
|
int> = 0>
|
||||||
|
void from_json(const BasicJsonType& j, ArithmeticType& val)
|
||||||
|
{
|
||||||
|
switch (static_cast<value_t>(j))
|
||||||
|
{
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
{
|
||||||
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case value_t::number_integer:
|
||||||
|
{
|
||||||
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case value_t::number_float:
|
||||||
|
{
|
||||||
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case value_t::boolean:
|
||||||
|
{
|
||||||
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename A1, typename A2>
|
||||||
|
void from_json(const BasicJsonType& j, std::pair<A1, A2>& p)
|
||||||
|
{
|
||||||
|
p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
|
||||||
|
void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...>)
|
||||||
|
{
|
||||||
|
t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename... Args>
|
||||||
|
void from_json(const BasicJsonType& j, std::tuple<Args...>& t)
|
||||||
|
{
|
||||||
|
from_json_tuple_impl(j, t, index_sequence_for<Args...> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct from_json_fn
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
auto call(const BasicJsonType& j, T& val, priority_tag<1> /*unused*/) const
|
||||||
|
noexcept(noexcept(from_json(j, val)))
|
||||||
|
-> decltype(from_json(j, val), void())
|
||||||
|
{
|
||||||
|
return from_json(j, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
void call(const BasicJsonType& /*unused*/, T& /*unused*/, priority_tag<0> /*unused*/) const noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(BasicJsonType) == 0,
|
||||||
|
"could not find from_json() method in T's namespace");
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
// MSVC does not show a stacktrace for the above assert
|
||||||
|
using decayed = uncvref_t<T>;
|
||||||
|
static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
|
||||||
|
"forcing MSVC stacktrace to show which T we're talking about.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
void operator()(const BasicJsonType& j, T& val) const
|
||||||
|
noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1> {})))
|
||||||
|
{
|
||||||
|
return call(j, val, priority_tag<1> {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// namespace to hold default `from_json` function
|
||||||
|
/// to see why this is required:
|
||||||
|
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;
|
||||||
|
}
|
||||||
|
}
|
338
develop/detail/conversions/to_json.hpp
Normal file
338
develop/detail/conversions/to_json.hpp
Normal file
|
@ -0,0 +1,338 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ciso646> // or, and, not
|
||||||
|
#include <iterator> // begin, end
|
||||||
|
#include <tuple> // tuple, get
|
||||||
|
#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
|
||||||
|
#include <utility> // move, forward, declval, pair
|
||||||
|
#include <valarray> // valarray
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
|
#include "detail/meta.hpp"
|
||||||
|
#include "detail/value_t.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
//////////////////
|
||||||
|
// constructors //
|
||||||
|
//////////////////
|
||||||
|
|
||||||
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct external_constructor<value_t::boolean>
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
||||||
|
{
|
||||||
|
j.m_type = value_t::boolean;
|
||||||
|
j.m_value = b;
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct external_constructor<value_t::string>
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::string;
|
||||||
|
j.m_value = s;
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::string;
|
||||||
|
j.m_value = std::move(s);
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct external_constructor<value_t::number_float>
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
||||||
|
{
|
||||||
|
j.m_type = value_t::number_float;
|
||||||
|
j.m_value = val;
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct external_constructor<value_t::number_unsigned>
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
||||||
|
{
|
||||||
|
j.m_type = value_t::number_unsigned;
|
||||||
|
j.m_value = val;
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct external_constructor<value_t::number_integer>
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
||||||
|
{
|
||||||
|
j.m_type = value_t::number_integer;
|
||||||
|
j.m_value = val;
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct external_constructor<value_t::array>
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::array;
|
||||||
|
j.m_value = arr;
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::array;
|
||||||
|
j.m_value = std::move(arr);
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||||
|
enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
|
||||||
|
int> = 0>
|
||||||
|
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
j.m_type = value_t::array;
|
||||||
|
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::array;
|
||||||
|
j.m_value = value_t::array;
|
||||||
|
j.m_value.array->reserve(arr.size());
|
||||||
|
for (const bool x : arr)
|
||||||
|
{
|
||||||
|
j.m_value.array->push_back(x);
|
||||||
|
}
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T,
|
||||||
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::array;
|
||||||
|
j.m_value = value_t::array;
|
||||||
|
j.m_value.array->resize(arr.size());
|
||||||
|
std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct external_constructor<value_t::object>
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::object;
|
||||||
|
j.m_value = obj;
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
|
{
|
||||||
|
j.m_type = value_t::object;
|
||||||
|
j.m_value = std::move(obj);
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||||
|
enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>
|
||||||
|
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
|
||||||
|
{
|
||||||
|
using std::begin;
|
||||||
|
using std::end;
|
||||||
|
|
||||||
|
j.m_type = value_t::object;
|
||||||
|
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||||
|
j.assert_invariant();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////
|
||||||
|
// to_json //
|
||||||
|
/////////////
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T,
|
||||||
|
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, T b) noexcept
|
||||||
|
{
|
||||||
|
external_constructor<value_t::boolean>::construct(j, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleString,
|
||||||
|
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, const CompatibleString& s)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::string>::construct(j, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::string>::construct(j, std::move(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename FloatType,
|
||||||
|
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, FloatType val) noexcept
|
||||||
|
{
|
||||||
|
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
||||||
|
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
||||||
|
{
|
||||||
|
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
||||||
|
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
||||||
|
{
|
||||||
|
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename EnumType,
|
||||||
|
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, EnumType e) noexcept
|
||||||
|
{
|
||||||
|
using underlying_type = typename std::underlying_type<EnumType>::type;
|
||||||
|
external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void to_json(BasicJsonType& j, const std::vector<bool>& e)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::array>::construct(j, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||||
|
enable_if_t<is_compatible_array_type<BasicJsonType, CompatibleArrayType>::value or
|
||||||
|
std::is_same<typename BasicJsonType::array_t, CompatibleArrayType>::value,
|
||||||
|
int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::array>::construct(j, arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T,
|
||||||
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, std::valarray<T> arr)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::array>::construct(j, std::move(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::array>::construct(j, std::move(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||||
|
enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::object>::construct(j, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::object>::construct(j, std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T, std::size_t N,
|
||||||
|
enable_if_t<not std::is_constructible<typename BasicJsonType::string_t, T (&)[N]>::value, int> = 0>
|
||||||
|
void to_json(BasicJsonType& j, T (&arr)[N])
|
||||||
|
{
|
||||||
|
external_constructor<value_t::array>::construct(j, arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename... Args>
|
||||||
|
void to_json(BasicJsonType& j, const std::pair<Args...>& p)
|
||||||
|
{
|
||||||
|
j = {p.first, p.second};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
|
||||||
|
void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...>)
|
||||||
|
{
|
||||||
|
j = {std::get<Idx>(t)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename... Args>
|
||||||
|
void to_json(BasicJsonType& j, const std::tuple<Args...>& t)
|
||||||
|
{
|
||||||
|
to_json_tuple_impl(j, t, index_sequence_for<Args...> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct to_json_fn
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
auto call(BasicJsonType& j, T&& val, priority_tag<1> /*unused*/) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
|
||||||
|
-> decltype(to_json(j, std::forward<T>(val)), void())
|
||||||
|
{
|
||||||
|
return to_json(j, std::forward<T>(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
void call(BasicJsonType& /*unused*/, T&& /*unused*/, priority_tag<0> /*unused*/) const noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(BasicJsonType) == 0,
|
||||||
|
"could not find to_json() method in T's namespace");
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
// MSVC does not show a stacktrace for the above assert
|
||||||
|
using decayed = uncvref_t<T>;
|
||||||
|
static_assert(sizeof(typename decayed::force_msvc_stacktrace) == 0,
|
||||||
|
"forcing MSVC stacktrace to show which T we're talking about.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
void operator()(BasicJsonType& j, T&& val) const
|
||||||
|
noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1> {})))
|
||||||
|
{
|
||||||
|
return call(j, std::forward<T>(val), priority_tag<1> {});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// namespace to hold default `to_json` function
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;
|
||||||
|
}
|
||||||
|
}
|
331
develop/detail/exceptions.hpp
Normal file
331
develop/detail/exceptions.hpp
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
#ifndef NLOHMANN_JSON_DETAIL_EXCEPTIONS_HPP
|
||||||
|
#define NLOHMANN_JSON_DETAIL_EXCEPTIONS_HPP
|
||||||
|
|
||||||
|
#include <exception> // exception
|
||||||
|
#include <stdexcept> // runtime_error
|
||||||
|
#include <string> // to_string
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
////////////////
|
||||||
|
// exceptions //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief general exception of the @ref basic_json class
|
||||||
|
|
||||||
|
This class is an extension of `std::exception` objects with a member @a id for
|
||||||
|
exception ids. It is used as the base class for all exceptions thrown by the
|
||||||
|
@ref basic_json class. This class can hence be used as "wildcard" to catch
|
||||||
|
exceptions.
|
||||||
|
|
||||||
|
Subclasses:
|
||||||
|
- @ref parse_error for exceptions indicating a parse error
|
||||||
|
- @ref invalid_iterator for exceptions indicating errors with iterators
|
||||||
|
- @ref type_error for exceptions indicating executing a member function with
|
||||||
|
a wrong type
|
||||||
|
- @ref out_of_range for exceptions indicating access out of the defined range
|
||||||
|
- @ref other_error for exceptions indicating other library errors
|
||||||
|
|
||||||
|
@internal
|
||||||
|
@note To have nothrow-copy-constructible exceptions, we internally use
|
||||||
|
`std::runtime_error` which can cope with arbitrary-length error messages.
|
||||||
|
Intermediate strings are built with static functions and then passed to
|
||||||
|
the actual constructor.
|
||||||
|
@endinternal
|
||||||
|
|
||||||
|
@liveexample{The following code shows how arbitrary library exceptions can be
|
||||||
|
caught.,exception}
|
||||||
|
|
||||||
|
@since version 3.0.0
|
||||||
|
*/
|
||||||
|
class exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// returns the explanatory string
|
||||||
|
const char* what() const noexcept override
|
||||||
|
{
|
||||||
|
return m.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the id of the exception
|
||||||
|
const int id;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}
|
||||||
|
|
||||||
|
static std::string name(const std::string& ename, int id_)
|
||||||
|
{
|
||||||
|
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// an exception object as storage for error messages
|
||||||
|
std::runtime_error m;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief exception indicating a parse error
|
||||||
|
|
||||||
|
This exception is thrown by the library when a parse error occurs. Parse errors
|
||||||
|
can occur during the deserialization of JSON text, CBOR, MessagePack, as well
|
||||||
|
as when using JSON Patch.
|
||||||
|
|
||||||
|
Member @a byte holds the byte index of the last read character in the input
|
||||||
|
file.
|
||||||
|
|
||||||
|
Exceptions have ids 1xx.
|
||||||
|
|
||||||
|
name / id | example message | description
|
||||||
|
------------------------------ | --------------- | -------------------------
|
||||||
|
json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.
|
||||||
|
json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.
|
||||||
|
json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.
|
||||||
|
json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.
|
||||||
|
json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors.
|
||||||
|
json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.
|
||||||
|
json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.
|
||||||
|
json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
|
||||||
|
json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.
|
||||||
|
json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
|
||||||
|
json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.
|
||||||
|
json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.
|
||||||
|
|
||||||
|
@note For an input with n bytes, 1 is the index of the first character and n+1
|
||||||
|
is the index of the terminating null byte or the end of file. This also
|
||||||
|
holds true when reading a byte vector (CBOR or MessagePack).
|
||||||
|
|
||||||
|
@liveexample{The following code shows how a `parse_error` exception can be
|
||||||
|
caught.,parse_error}
|
||||||
|
|
||||||
|
@sa @ref exception for the base class of the library exceptions
|
||||||
|
@sa @ref invalid_iterator for exceptions indicating errors with iterators
|
||||||
|
@sa @ref type_error for exceptions indicating executing a member function with
|
||||||
|
a wrong type
|
||||||
|
@sa @ref out_of_range for exceptions indicating access out of the defined range
|
||||||
|
@sa @ref other_error for exceptions indicating other library errors
|
||||||
|
|
||||||
|
@since version 3.0.0
|
||||||
|
*/
|
||||||
|
class parse_error : public exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
@brief create a parse error exception
|
||||||
|
@param[in] id_ the id of the exception
|
||||||
|
@param[in] byte_ the byte index where the error occurred (or 0 if the
|
||||||
|
position cannot be determined)
|
||||||
|
@param[in] what_arg the explanatory string
|
||||||
|
@return parse_error object
|
||||||
|
*/
|
||||||
|
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)
|
||||||
|
{
|
||||||
|
std::string w = exception::name("parse_error", id_) + "parse error" +
|
||||||
|
(byte_ != 0 ? (" at " + std::to_string(byte_)) : "") +
|
||||||
|
": " + what_arg;
|
||||||
|
return parse_error(id_, byte_, w.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief byte index of the parse error
|
||||||
|
|
||||||
|
The byte index of the last read character in the input file.
|
||||||
|
|
||||||
|
@note For an input with n bytes, 1 is the index of the first character and
|
||||||
|
n+1 is the index of the terminating null byte or the end of file.
|
||||||
|
This also holds true when reading a byte vector (CBOR or MessagePack).
|
||||||
|
*/
|
||||||
|
const std::size_t byte;
|
||||||
|
|
||||||
|
private:
|
||||||
|
parse_error(int id_, std::size_t byte_, const char* what_arg)
|
||||||
|
: exception(id_, what_arg), byte(byte_) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief exception indicating errors with iterators
|
||||||
|
|
||||||
|
This exception is thrown if iterators passed to a library function do not match
|
||||||
|
the expected semantics.
|
||||||
|
|
||||||
|
Exceptions have ids 2xx.
|
||||||
|
|
||||||
|
name / id | example message | description
|
||||||
|
----------------------------------- | --------------- | -------------------------
|
||||||
|
json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
|
||||||
|
json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
|
||||||
|
json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
|
||||||
|
json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
|
||||||
|
json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
|
||||||
|
json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
|
||||||
|
json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
|
||||||
|
json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
|
||||||
|
json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
|
||||||
|
json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
|
||||||
|
json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.
|
||||||
|
json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.
|
||||||
|
json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.
|
||||||
|
json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
|
||||||
|
|
||||||
|
@liveexample{The following code shows how an `invalid_iterator` exception can be
|
||||||
|
caught.,invalid_iterator}
|
||||||
|
|
||||||
|
@sa @ref exception for the base class of the library exceptions
|
||||||
|
@sa @ref parse_error for exceptions indicating a parse error
|
||||||
|
@sa @ref type_error for exceptions indicating executing a member function with
|
||||||
|
a wrong type
|
||||||
|
@sa @ref out_of_range for exceptions indicating access out of the defined range
|
||||||
|
@sa @ref other_error for exceptions indicating other library errors
|
||||||
|
|
||||||
|
@since version 3.0.0
|
||||||
|
*/
|
||||||
|
class invalid_iterator : public exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static invalid_iterator create(int id_, const std::string& what_arg)
|
||||||
|
{
|
||||||
|
std::string w = exception::name("invalid_iterator", id_) + what_arg;
|
||||||
|
return invalid_iterator(id_, w.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
invalid_iterator(int id_, const char* what_arg)
|
||||||
|
: exception(id_, what_arg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief exception indicating executing a member function with a wrong type
|
||||||
|
|
||||||
|
This exception is thrown in case of a type error; that is, a library function is
|
||||||
|
executed on a JSON value whose type does not match the expected semantics.
|
||||||
|
|
||||||
|
Exceptions have ids 3xx.
|
||||||
|
|
||||||
|
name / id | example message | description
|
||||||
|
----------------------------- | --------------- | -------------------------
|
||||||
|
json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.
|
||||||
|
json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.
|
||||||
|
json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&.
|
||||||
|
json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.
|
||||||
|
json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.
|
||||||
|
json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.
|
||||||
|
json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.
|
||||||
|
json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |
|
||||||
|
|
||||||
|
@liveexample{The following code shows how a `type_error` exception can be
|
||||||
|
caught.,type_error}
|
||||||
|
|
||||||
|
@sa @ref exception for the base class of the library exceptions
|
||||||
|
@sa @ref parse_error for exceptions indicating a parse error
|
||||||
|
@sa @ref invalid_iterator for exceptions indicating errors with iterators
|
||||||
|
@sa @ref out_of_range for exceptions indicating access out of the defined range
|
||||||
|
@sa @ref other_error for exceptions indicating other library errors
|
||||||
|
|
||||||
|
@since version 3.0.0
|
||||||
|
*/
|
||||||
|
class type_error : public exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static type_error create(int id_, const std::string& what_arg)
|
||||||
|
{
|
||||||
|
std::string w = exception::name("type_error", id_) + what_arg;
|
||||||
|
return type_error(id_, w.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief exception indicating access out of the defined range
|
||||||
|
|
||||||
|
This exception is thrown in case a library function is called on an input
|
||||||
|
parameter that exceeds the expected range, for instance in case of array
|
||||||
|
indices or nonexisting object keys.
|
||||||
|
|
||||||
|
Exceptions have ids 4xx.
|
||||||
|
|
||||||
|
name / id | example message | description
|
||||||
|
------------------------------- | --------------- | -------------------------
|
||||||
|
json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.
|
||||||
|
json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.
|
||||||
|
json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.
|
||||||
|
json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.
|
||||||
|
json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.
|
||||||
|
json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.
|
||||||
|
|
||||||
|
@liveexample{The following code shows how an `out_of_range` exception can be
|
||||||
|
caught.,out_of_range}
|
||||||
|
|
||||||
|
@sa @ref exception for the base class of the library exceptions
|
||||||
|
@sa @ref parse_error for exceptions indicating a parse error
|
||||||
|
@sa @ref invalid_iterator for exceptions indicating errors with iterators
|
||||||
|
@sa @ref type_error for exceptions indicating executing a member function with
|
||||||
|
a wrong type
|
||||||
|
@sa @ref other_error for exceptions indicating other library errors
|
||||||
|
|
||||||
|
@since version 3.0.0
|
||||||
|
*/
|
||||||
|
class out_of_range : public exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static out_of_range create(int id_, const std::string& what_arg)
|
||||||
|
{
|
||||||
|
std::string w = exception::name("out_of_range", id_) + what_arg;
|
||||||
|
return out_of_range(id_, w.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief exception indicating other library errors
|
||||||
|
|
||||||
|
This exception is thrown in case of errors that cannot be classified with the
|
||||||
|
other exception types.
|
||||||
|
|
||||||
|
Exceptions have ids 5xx.
|
||||||
|
|
||||||
|
name / id | example message | description
|
||||||
|
------------------------------ | --------------- | -------------------------
|
||||||
|
json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.
|
||||||
|
|
||||||
|
@sa @ref exception for the base class of the library exceptions
|
||||||
|
@sa @ref parse_error for exceptions indicating a parse error
|
||||||
|
@sa @ref invalid_iterator for exceptions indicating errors with iterators
|
||||||
|
@sa @ref type_error for exceptions indicating executing a member function with
|
||||||
|
a wrong type
|
||||||
|
@sa @ref out_of_range for exceptions indicating access out of the defined range
|
||||||
|
|
||||||
|
@liveexample{The following code shows how an `other_error` exception can be
|
||||||
|
caught.,other_error}
|
||||||
|
|
||||||
|
@since version 3.0.0
|
||||||
|
*/
|
||||||
|
class other_error : public exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static other_error create(int id_, const std::string& what_arg)
|
||||||
|
{
|
||||||
|
std::string w = exception::name("other_error", id_) + what_arg;
|
||||||
|
return other_error(id_, w.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
25
develop/detail/iterators/internal_iterator.hpp
Normal file
25
develop/detail/iterators/internal_iterator.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "detail/iterators/primitive_iterator.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
@brief an iterator value
|
||||||
|
|
||||||
|
@note This structure could easily be a union, but MSVC currently does not allow
|
||||||
|
unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
|
||||||
|
*/
|
||||||
|
template<typename BasicJsonType> struct internal_iterator
|
||||||
|
{
|
||||||
|
/// iterator for JSON objects
|
||||||
|
typename BasicJsonType::object_t::iterator object_iterator {};
|
||||||
|
/// iterator for JSON arrays
|
||||||
|
typename BasicJsonType::array_t::iterator array_iterator {};
|
||||||
|
/// generic iterator for all other types
|
||||||
|
primitive_iterator_t primitive_iterator {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
614
develop/detail/iterators/iter_impl.hpp
Normal file
614
develop/detail/iterators/iter_impl.hpp
Normal file
|
@ -0,0 +1,614 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ciso646> // not
|
||||||
|
#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
|
||||||
|
#include <type_traits> // conditional, is_const, remove_const
|
||||||
|
|
||||||
|
#include "detail/exceptions.hpp"
|
||||||
|
#include "detail/iterators/internal_iterator.hpp"
|
||||||
|
#include "detail/iterators/primitive_iterator.hpp"
|
||||||
|
#include "detail/macro_scope.hpp"
|
||||||
|
#include "detail/meta.hpp"
|
||||||
|
#include "detail/value_t.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
// forward declare, to be able to friend it later on
|
||||||
|
template<typename IteratorType> class iteration_proxy;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief a template for a bidirectional iterator for the @ref basic_json class
|
||||||
|
|
||||||
|
This class implements a both iterators (iterator and const_iterator) for the
|
||||||
|
@ref basic_json class.
|
||||||
|
|
||||||
|
@note An iterator is called *initialized* when a pointer to a JSON value has
|
||||||
|
been set (e.g., by a constructor or a copy assignment). If the iterator is
|
||||||
|
default-constructed, it is *uninitialized* and most methods are undefined.
|
||||||
|
**The library uses assertions to detect calls on uninitialized iterators.**
|
||||||
|
|
||||||
|
@requirement The class satisfies the following concept requirements:
|
||||||
|
-
|
||||||
|
[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
|
||||||
|
The iterator that can be moved can be moved in both directions (i.e.
|
||||||
|
incremented and decremented).
|
||||||
|
|
||||||
|
@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
|
||||||
|
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
|
||||||
|
*/
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
class iter_impl
|
||||||
|
{
|
||||||
|
/// allow basic_json to access private members
|
||||||
|
friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
|
||||||
|
friend BasicJsonType;
|
||||||
|
friend iteration_proxy<iter_impl>;
|
||||||
|
|
||||||
|
using object_t = typename BasicJsonType::object_t;
|
||||||
|
using array_t = typename BasicJsonType::array_t;
|
||||||
|
// make sure BasicJsonType is basic_json or const basic_json
|
||||||
|
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
||||||
|
"iter_impl only accepts (const) basic_json");
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
||||||
|
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
|
||||||
|
/// A user-defined iterator should provide publicly accessible typedefs named
|
||||||
|
/// iterator_category, value_type, difference_type, pointer, and reference.
|
||||||
|
/// Note that value_type is required to be non-const, even for constant iterators.
|
||||||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||||||
|
|
||||||
|
/// the type of the values when the iterator is dereferenced
|
||||||
|
using value_type = typename BasicJsonType::value_type;
|
||||||
|
/// a type to represent differences between iterators
|
||||||
|
using difference_type = typename BasicJsonType::difference_type;
|
||||||
|
/// defines a pointer to the type iterated over (value_type)
|
||||||
|
using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
|
||||||
|
typename BasicJsonType::const_pointer,
|
||||||
|
typename BasicJsonType::pointer>::type;
|
||||||
|
/// defines a reference to the type iterated over (value_type)
|
||||||
|
using reference =
|
||||||
|
typename std::conditional<std::is_const<BasicJsonType>::value,
|
||||||
|
typename BasicJsonType::const_reference,
|
||||||
|
typename BasicJsonType::reference>::type;
|
||||||
|
|
||||||
|
/// default constructor
|
||||||
|
iter_impl() = default;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief constructor for a given JSON instance
|
||||||
|
@param[in] object pointer to a JSON object for this iterator
|
||||||
|
@pre object != nullptr
|
||||||
|
@post The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
explicit iter_impl(pointer object) noexcept : m_object(object)
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
m_it.object_iterator = typename object_t::iterator();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
m_it.array_iterator = typename array_t::iterator();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
m_it.primitive_iterator = primitive_iterator_t();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@note The conventional copy constructor and copy assignment are implicitly
|
||||||
|
defined. Combined with the following converting constructor and
|
||||||
|
assignment, they support: (1) copy from iterator to iterator, (2)
|
||||||
|
copy from const iterator to const iterator, and (3) conversion from
|
||||||
|
iterator to const iterator. However conversion from const iterator
|
||||||
|
to iterator is not defined.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief converting constructor
|
||||||
|
@param[in] other non-const iterator to copy from
|
||||||
|
@note It is not checked whether @a other is initialized.
|
||||||
|
*/
|
||||||
|
iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
|
||||||
|
: m_object(other.m_object), m_it(other.m_it) {}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief converting assignment
|
||||||
|
@param[in,out] other non-const iterator to copy from
|
||||||
|
@return const/non-const iterator
|
||||||
|
@note It is not checked whether @a other is initialized.
|
||||||
|
*/
|
||||||
|
iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
|
||||||
|
{
|
||||||
|
m_object = other.m_object;
|
||||||
|
m_it = other.m_it;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
@brief set the iterator to the first value
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
void set_begin() noexcept
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
m_it.object_iterator = m_object->m_value.object->begin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
m_it.array_iterator = m_object->m_value.array->begin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::null:
|
||||||
|
{
|
||||||
|
// set to end so begin()==end() is true: null is empty
|
||||||
|
m_it.primitive_iterator.set_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
m_it.primitive_iterator.set_begin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief set the iterator past the last value
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
void set_end() noexcept
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
m_it.object_iterator = m_object->m_value.object->end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
m_it.array_iterator = m_object->m_value.array->end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
m_it.primitive_iterator.set_end();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
@brief return a reference to the value pointed to by the iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
assert(m_it.object_iterator != m_object->m_value.object->end());
|
||||||
|
return m_it.object_iterator->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
assert(m_it.array_iterator != m_object->m_value.array->end());
|
||||||
|
return *m_it.array_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::null:
|
||||||
|
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
|
||||||
|
{
|
||||||
|
return *m_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief dereference the iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
pointer operator->() const
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
assert(m_it.object_iterator != m_object->m_value.object->end());
|
||||||
|
return &(m_it.object_iterator->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
assert(m_it.array_iterator != m_object->m_value.array->end());
|
||||||
|
return &*m_it.array_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))
|
||||||
|
{
|
||||||
|
return m_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief post-increment (it++)
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl const operator++(int)
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
++(*this);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief pre-increment (++it)
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl& operator++()
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
std::advance(m_it.object_iterator, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
std::advance(m_it.array_iterator, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
++m_it.primitive_iterator;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief post-decrement (it--)
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl const operator--(int)
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
--(*this);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief pre-decrement (--it)
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl& operator--()
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
std::advance(m_it.object_iterator, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
std::advance(m_it.array_iterator, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
--m_it.primitive_iterator;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief comparison: equal
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
bool operator==(const iter_impl& other) const
|
||||||
|
{
|
||||||
|
// if objects are not the same, the comparison is undefined
|
||||||
|
if (JSON_UNLIKELY(m_object != other.m_object))
|
||||||
|
{
|
||||||
|
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
return (m_it.object_iterator == other.m_it.object_iterator);
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
return (m_it.array_iterator == other.m_it.array_iterator);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief comparison: not equal
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
bool operator!=(const iter_impl& other) const
|
||||||
|
{
|
||||||
|
return not operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief comparison: smaller
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
bool operator<(const iter_impl& other) const
|
||||||
|
{
|
||||||
|
// if objects are not the same, the comparison is undefined
|
||||||
|
if (JSON_UNLIKELY(m_object != other.m_object))
|
||||||
|
{
|
||||||
|
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers"));
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators"));
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
return (m_it.array_iterator < other.m_it.array_iterator);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief comparison: less than or equal
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
bool operator<=(const iter_impl& other) const
|
||||||
|
{
|
||||||
|
return not other.operator < (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief comparison: greater than
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
bool operator>(const iter_impl& other) const
|
||||||
|
{
|
||||||
|
return not operator<=(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief comparison: greater than or equal
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
bool operator>=(const iter_impl& other) const
|
||||||
|
{
|
||||||
|
return not operator<(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief add to iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl& operator+=(difference_type i)
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
std::advance(m_it.array_iterator, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
m_it.primitive_iterator += i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief subtract from iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl& operator-=(difference_type i)
|
||||||
|
{
|
||||||
|
return operator+=(-i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief add to iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl operator+(difference_type i) const
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
result += i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief addition of distance and iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
friend iter_impl operator+(difference_type i, const iter_impl& it)
|
||||||
|
{
|
||||||
|
auto result = it;
|
||||||
|
result += i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief subtract from iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
iter_impl operator-(difference_type i) const
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
result -= i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief return difference
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
difference_type operator-(const iter_impl& other) const
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators"));
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
return m_it.array_iterator - other.m_it.array_iterator;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return m_it.primitive_iterator - other.m_it.primitive_iterator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief access to successor
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
reference operator[](difference_type n) const
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
switch (m_object->m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators"));
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
return *std::next(m_it.array_iterator, n);
|
||||||
|
|
||||||
|
case value_t::null:
|
||||||
|
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n))
|
||||||
|
{
|
||||||
|
return *m_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_THROW(invalid_iterator::create(214, "cannot get value"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief return the key of an object iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
typename object_t::key_type key() const
|
||||||
|
{
|
||||||
|
assert(m_object != nullptr);
|
||||||
|
|
||||||
|
if (JSON_LIKELY(m_object->is_object()))
|
||||||
|
{
|
||||||
|
return m_it.object_iterator->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief return the value of an iterator
|
||||||
|
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
||||||
|
*/
|
||||||
|
reference value() const
|
||||||
|
{
|
||||||
|
return operator*();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// associated JSON instance
|
||||||
|
pointer m_object = nullptr;
|
||||||
|
/// the actual iterator of the associated instance
|
||||||
|
internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
98
develop/detail/iterators/iteration_proxy.hpp
Normal file
98
develop/detail/iterators/iteration_proxy.hpp
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <string> // string, to_string
|
||||||
|
|
||||||
|
#include "detail/value_t.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/// proxy class for the iterator_wrapper functions
|
||||||
|
template<typename IteratorType> class iteration_proxy
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/// helper class for iteration
|
||||||
|
class iteration_proxy_internal
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/// the iterator
|
||||||
|
IteratorType anchor;
|
||||||
|
/// an index for arrays (used to create key names)
|
||||||
|
std::size_t array_index = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {}
|
||||||
|
|
||||||
|
/// dereference operator (needed for range-based for)
|
||||||
|
iteration_proxy_internal& operator*()
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// increment operator (needed for range-based for)
|
||||||
|
iteration_proxy_internal& operator++()
|
||||||
|
{
|
||||||
|
++anchor;
|
||||||
|
++array_index;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inequality operator (needed for range-based for)
|
||||||
|
bool operator!=(const iteration_proxy_internal& o) const noexcept
|
||||||
|
{
|
||||||
|
return anchor != o.anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return key of the iterator
|
||||||
|
std::string key() const
|
||||||
|
{
|
||||||
|
assert(anchor.m_object != nullptr);
|
||||||
|
|
||||||
|
switch (anchor.m_object->type())
|
||||||
|
{
|
||||||
|
// use integer array index as key
|
||||||
|
case value_t::array:
|
||||||
|
return std::to_string(array_index);
|
||||||
|
|
||||||
|
// use key from the object
|
||||||
|
case value_t::object:
|
||||||
|
return anchor.key();
|
||||||
|
|
||||||
|
// use an empty key for all primitive types
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return value of the iterator
|
||||||
|
typename IteratorType::reference value() const
|
||||||
|
{
|
||||||
|
return anchor.value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the container to iterate
|
||||||
|
typename IteratorType::reference container;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// construct iteration proxy from a container
|
||||||
|
explicit iteration_proxy(typename IteratorType::reference cont)
|
||||||
|
: container(cont) {}
|
||||||
|
|
||||||
|
/// return iterator begin (needed for range-based for)
|
||||||
|
iteration_proxy_internal begin() noexcept
|
||||||
|
{
|
||||||
|
return iteration_proxy_internal(container.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return iterator end (needed for range-based for)
|
||||||
|
iteration_proxy_internal end() noexcept
|
||||||
|
{
|
||||||
|
return iteration_proxy_internal(container.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
119
develop/detail/iterators/json_reverse_iterator.hpp
Normal file
119
develop/detail/iterators/json_reverse_iterator.hpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef> // ptrdiff_t
|
||||||
|
#include <iterator> // reverse_iterator
|
||||||
|
#include <utility> // declval
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
//////////////////////
|
||||||
|
// reverse_iterator //
|
||||||
|
//////////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief a template for a reverse iterator class
|
||||||
|
|
||||||
|
@tparam Base the base iterator type to reverse. Valid types are @ref
|
||||||
|
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
|
||||||
|
create @ref const_reverse_iterator).
|
||||||
|
|
||||||
|
@requirement The class satisfies the following concept requirements:
|
||||||
|
-
|
||||||
|
[BidirectionalIterator](http://en.cppreference.com/w/cpp/concept/BidirectionalIterator):
|
||||||
|
The iterator that can be moved can be moved in both directions (i.e.
|
||||||
|
incremented and decremented).
|
||||||
|
- [OutputIterator](http://en.cppreference.com/w/cpp/concept/OutputIterator):
|
||||||
|
It is possible to write to the pointed-to element (only if @a Base is
|
||||||
|
@ref iterator).
|
||||||
|
|
||||||
|
@since version 1.0.0
|
||||||
|
*/
|
||||||
|
template<typename Base>
|
||||||
|
class json_reverse_iterator : public std::reverse_iterator<Base>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
/// shortcut to the reverse iterator adapter
|
||||||
|
using base_iterator = std::reverse_iterator<Base>;
|
||||||
|
/// the reference type for the pointed-to element
|
||||||
|
using reference = typename Base::reference;
|
||||||
|
|
||||||
|
/// create reverse iterator from iterator
|
||||||
|
json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
|
||||||
|
: base_iterator(it) {}
|
||||||
|
|
||||||
|
/// create reverse iterator from base class
|
||||||
|
json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
|
||||||
|
|
||||||
|
/// post-increment (it++)
|
||||||
|
json_reverse_iterator const operator++(int)
|
||||||
|
{
|
||||||
|
return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// pre-increment (++it)
|
||||||
|
json_reverse_iterator& operator++()
|
||||||
|
{
|
||||||
|
return static_cast<json_reverse_iterator&>(base_iterator::operator++());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// post-decrement (it--)
|
||||||
|
json_reverse_iterator const operator--(int)
|
||||||
|
{
|
||||||
|
return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// pre-decrement (--it)
|
||||||
|
json_reverse_iterator& operator--()
|
||||||
|
{
|
||||||
|
return static_cast<json_reverse_iterator&>(base_iterator::operator--());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add to iterator
|
||||||
|
json_reverse_iterator& operator+=(difference_type i)
|
||||||
|
{
|
||||||
|
return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add to iterator
|
||||||
|
json_reverse_iterator operator+(difference_type i) const
|
||||||
|
{
|
||||||
|
return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// subtract from iterator
|
||||||
|
json_reverse_iterator operator-(difference_type i) const
|
||||||
|
{
|
||||||
|
return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return difference
|
||||||
|
difference_type operator-(const json_reverse_iterator& other) const
|
||||||
|
{
|
||||||
|
return base_iterator(*this) - base_iterator(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// access to successor
|
||||||
|
reference operator[](difference_type n) const
|
||||||
|
{
|
||||||
|
return *(this->operator+(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the key of an object iterator
|
||||||
|
auto key() const -> decltype(std::declval<Base>().key())
|
||||||
|
{
|
||||||
|
auto it = --this->base();
|
||||||
|
return it.key();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the value of an iterator
|
||||||
|
reference value() const
|
||||||
|
{
|
||||||
|
auto it = --this->base();
|
||||||
|
return it.operator * ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
128
develop/detail/iterators/primitive_iterator.hpp
Normal file
128
develop/detail/iterators/primitive_iterator.hpp
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ciso646> // not
|
||||||
|
#include <cstddef> // ptrdiff_t
|
||||||
|
#include <limits> // numeric_limits
|
||||||
|
#include <ostream> // ostream
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
@brief an iterator for primitive JSON types
|
||||||
|
|
||||||
|
This class models an iterator for primitive JSON types (boolean, number,
|
||||||
|
string). It's only purpose is to allow the iterator/const_iterator classes
|
||||||
|
to "iterate" over primitive values. Internally, the iterator is modeled by
|
||||||
|
a `difference_type` variable. Value begin_value (`0`) models the begin,
|
||||||
|
end_value (`1`) models past the end.
|
||||||
|
*/
|
||||||
|
class primitive_iterator_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
|
||||||
|
constexpr difference_type get_value() const noexcept
|
||||||
|
{
|
||||||
|
return m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set iterator to a defined beginning
|
||||||
|
void set_begin() noexcept
|
||||||
|
{
|
||||||
|
m_it = begin_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set iterator to a defined past the end
|
||||||
|
void set_end() noexcept
|
||||||
|
{
|
||||||
|
m_it = end_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return whether the iterator can be dereferenced
|
||||||
|
constexpr bool is_begin() const noexcept
|
||||||
|
{
|
||||||
|
return m_it == begin_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return whether the iterator is at end
|
||||||
|
constexpr bool is_end() const noexcept
|
||||||
|
{
|
||||||
|
return m_it == end_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
||||||
|
{
|
||||||
|
return lhs.m_it == rhs.m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
||||||
|
{
|
||||||
|
return lhs.m_it < rhs.m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive_iterator_t operator+(difference_type i)
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
result += i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
||||||
|
{
|
||||||
|
return lhs.m_it - rhs.m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& os, primitive_iterator_t it)
|
||||||
|
{
|
||||||
|
return os << it.m_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive_iterator_t& operator++()
|
||||||
|
{
|
||||||
|
++m_it;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive_iterator_t const operator++(int)
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
m_it++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive_iterator_t& operator--()
|
||||||
|
{
|
||||||
|
--m_it;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive_iterator_t const operator--(int)
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
m_it--;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive_iterator_t& operator+=(difference_type n)
|
||||||
|
{
|
||||||
|
m_it += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitive_iterator_t& operator-=(difference_type n)
|
||||||
|
{
|
||||||
|
m_it -= n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr difference_type begin_value = 0;
|
||||||
|
static constexpr difference_type end_value = begin_value + 1;
|
||||||
|
|
||||||
|
/// iterator as signed integer type
|
||||||
|
difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
63
develop/detail/json_ref.hpp
Normal file
63
develop/detail/json_ref.hpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
class json_ref
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = BasicJsonType;
|
||||||
|
|
||||||
|
json_ref(value_type&& value)
|
||||||
|
: owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
json_ref(const value_type& value)
|
||||||
|
: value_ref(const_cast<value_type*>(&value)), is_rvalue(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
json_ref(std::initializer_list<json_ref> init)
|
||||||
|
: owned_value(init), value_ref(&owned_value), is_rvalue(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
json_ref(Args&& ... args)
|
||||||
|
: owned_value(std::forward<Args>(args)...), value_ref(&owned_value), is_rvalue(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// class should be movable only
|
||||||
|
json_ref(json_ref&&) = default;
|
||||||
|
json_ref(const json_ref&) = delete;
|
||||||
|
json_ref& operator=(const json_ref&) = delete;
|
||||||
|
|
||||||
|
value_type moved_or_copied() const
|
||||||
|
{
|
||||||
|
if (is_rvalue)
|
||||||
|
{
|
||||||
|
return std::move(*value_ref);
|
||||||
|
}
|
||||||
|
return *value_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type const& operator*() const
|
||||||
|
{
|
||||||
|
return *static_cast<value_type const*>(value_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type const* operator->() const
|
||||||
|
{
|
||||||
|
return static_cast<value_type const*>(value_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable value_type owned_value = nullptr;
|
||||||
|
value_type* value_ref = nullptr;
|
||||||
|
const bool is_rvalue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
103
develop/detail/macro_scope.hpp
Normal file
103
develop/detail/macro_scope.hpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ciso646> // not
|
||||||
|
|
||||||
|
// This file contains all internal macro definitions
|
||||||
|
// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
|
||||||
|
|
||||||
|
// exclude unsupported compilers
|
||||||
|
#if defined(__clang__)
|
||||||
|
#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
|
||||||
|
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
|
||||||
|
#endif
|
||||||
|
#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
|
||||||
|
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
|
||||||
|
#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// disable float-equal warnings on GCC/clang
|
||||||
|
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// disable documentation warnings on clang
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wdocumentation"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// allow for portable deprecation warnings
|
||||||
|
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
|
||||||
|
#define JSON_DEPRECATED __attribute__((deprecated))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define JSON_DEPRECATED __declspec(deprecated)
|
||||||
|
#else
|
||||||
|
#define JSON_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// allow to disable exceptions
|
||||||
|
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(JSON_NOEXCEPTION)
|
||||||
|
#define JSON_THROW(exception) throw exception
|
||||||
|
#define JSON_TRY try
|
||||||
|
#define JSON_CATCH(exception) catch(exception)
|
||||||
|
#else
|
||||||
|
#define JSON_THROW(exception) std::abort()
|
||||||
|
#define JSON_TRY if(true)
|
||||||
|
#define JSON_CATCH(exception) if(false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// manual branch prediction
|
||||||
|
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
|
||||||
|
#define JSON_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||||
|
#define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||||
|
#else
|
||||||
|
#define JSON_LIKELY(x) x
|
||||||
|
#define JSON_UNLIKELY(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// C++ language standard detection
|
||||||
|
#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
|
||||||
|
#define JSON_HAS_CPP_17
|
||||||
|
#define JSON_HAS_CPP_14
|
||||||
|
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
|
||||||
|
#define JSON_HAS_CPP_14
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||||
|
// may be removed in the future once the class is split.
|
||||||
|
|
||||||
|
#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
|
||||||
|
template<template<typename, typename, typename...> class ObjectType, \
|
||||||
|
template<typename, typename...> class ArrayType, \
|
||||||
|
class StringType, class BooleanType, class NumberIntegerType, \
|
||||||
|
class NumberUnsignedType, class NumberFloatType, \
|
||||||
|
template<typename> class AllocatorType, \
|
||||||
|
template<typename, typename = void> class JSONSerializer>
|
||||||
|
|
||||||
|
#define NLOHMANN_BASIC_JSON_TPL \
|
||||||
|
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
|
||||||
|
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
|
||||||
|
AllocatorType, JSONSerializer>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief Helper to determine whether there's a key_type for T.
|
||||||
|
|
||||||
|
This helper is used to tell associative containers apart from other containers
|
||||||
|
such as sequence containers. For instance, `std::map` passes the test as it
|
||||||
|
contains a `mapped_type`, whereas `std::vector` fails the test.
|
||||||
|
|
||||||
|
@sa http://stackoverflow.com/a/7728728/266378
|
||||||
|
@since version 1.0.0, overworked in version 2.0.6
|
||||||
|
*/
|
||||||
|
#define NLOHMANN_JSON_HAS_HELPER(type) \
|
||||||
|
template<typename T> struct has_##type { \
|
||||||
|
private: \
|
||||||
|
template<typename U, typename = typename U::type> \
|
||||||
|
static int detect(U &&); \
|
||||||
|
static void detect(...); \
|
||||||
|
public: \
|
||||||
|
static constexpr bool value = \
|
||||||
|
std::is_integral<decltype(detect(std::declval<T>()))>::value; \
|
||||||
|
}
|
22
develop/detail/macro_unscope.hpp
Normal file
22
develop/detail/macro_unscope.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// restore GCC/clang diagnostic settings
|
||||||
|
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
#undef JSON_CATCH
|
||||||
|
#undef JSON_THROW
|
||||||
|
#undef JSON_TRY
|
||||||
|
#undef JSON_LIKELY
|
||||||
|
#undef JSON_UNLIKELY
|
||||||
|
#undef JSON_DEPRECATED
|
||||||
|
#undef JSON_HAS_CPP_14
|
||||||
|
#undef JSON_HAS_CPP_17
|
||||||
|
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||||
|
#undef NLOHMANN_BASIC_JSON_TPL
|
||||||
|
#undef NLOHMANN_JSON_HAS_HELPER
|
234
develop/detail/meta.hpp
Normal file
234
develop/detail/meta.hpp
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ciso646> // not
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <limits> // numeric_limits
|
||||||
|
#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
|
||||||
|
#include <utility> // declval
|
||||||
|
|
||||||
|
#include "json_fwd.hpp"
|
||||||
|
#include "detail/macro_scope.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
@brief detail namespace with internal helper functions
|
||||||
|
|
||||||
|
This namespace collects functions that should not be exposed,
|
||||||
|
implementations of some @ref basic_json methods, and meta-programming helpers.
|
||||||
|
|
||||||
|
@since version 2.1.0
|
||||||
|
*/
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/////////////
|
||||||
|
// helpers //
|
||||||
|
/////////////
|
||||||
|
|
||||||
|
template<typename> struct is_basic_json : std::false_type {};
|
||||||
|
|
||||||
|
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
||||||
|
struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
|
||||||
|
|
||||||
|
// alias templates to reduce boilerplate
|
||||||
|
template<bool B, typename T = void>
|
||||||
|
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||||
|
|
||||||
|
// implementation of C++14 index_sequence and affiliates
|
||||||
|
// source: https://stackoverflow.com/a/32223343
|
||||||
|
template<std::size_t... Ints>
|
||||||
|
struct index_sequence
|
||||||
|
{
|
||||||
|
using type = index_sequence;
|
||||||
|
using value_type = std::size_t;
|
||||||
|
static constexpr std::size_t size() noexcept
|
||||||
|
{
|
||||||
|
return sizeof...(Ints);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Sequence1, class Sequence2>
|
||||||
|
struct merge_and_renumber;
|
||||||
|
|
||||||
|
template<std::size_t... I1, std::size_t... I2>
|
||||||
|
struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
|
||||||
|
: index_sequence < I1..., (sizeof...(I1) + I2)... > {};
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
struct make_index_sequence
|
||||||
|
: merge_and_renumber < typename make_index_sequence < N / 2 >::type,
|
||||||
|
typename make_index_sequence < N - N / 2 >::type > {};
|
||||||
|
|
||||||
|
template<> struct make_index_sequence<0> : index_sequence<> {};
|
||||||
|
template<> struct make_index_sequence<1> : index_sequence<0> {};
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implementation of two C++17 constructs: conjunction, negation. This is needed
|
||||||
|
to avoid evaluating all the traits in a condition
|
||||||
|
|
||||||
|
For example: not std::is_same<void, T>::value and has_value_type<T>::value
|
||||||
|
will not compile when T = void (on MSVC at least). Whereas
|
||||||
|
conjunction<negation<std::is_same<void, T>>, has_value_type<T>>::value will
|
||||||
|
stop evaluating if negation<...>::value == false
|
||||||
|
|
||||||
|
Please note that those constructs must be used with caution, since symbols can
|
||||||
|
become very long quickly (which can slow down compilation and cause MSVC
|
||||||
|
internal compiler errors). Only use it when you have to (see example ahead).
|
||||||
|
*/
|
||||||
|
template<class...> struct conjunction : std::true_type {};
|
||||||
|
template<class B1> struct conjunction<B1> : B1 {};
|
||||||
|
template<class B1, class... Bn>
|
||||||
|
struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
|
||||||
|
|
||||||
|
template<class B> struct negation : std::integral_constant<bool, not B::value> {};
|
||||||
|
|
||||||
|
// dispatch utility (taken from ranges-v3)
|
||||||
|
template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
|
||||||
|
template<> struct priority_tag<0> {};
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// has_/is_ functions //
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
NLOHMANN_JSON_HAS_HELPER(mapped_type);
|
||||||
|
NLOHMANN_JSON_HAS_HELPER(key_type);
|
||||||
|
NLOHMANN_JSON_HAS_HELPER(value_type);
|
||||||
|
NLOHMANN_JSON_HAS_HELPER(iterator);
|
||||||
|
|
||||||
|
template<bool B, class RealType, class CompatibleObjectType>
|
||||||
|
struct is_compatible_object_type_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<class RealType, class CompatibleObjectType>
|
||||||
|
struct is_compatible_object_type_impl<true, RealType, CompatibleObjectType>
|
||||||
|
{
|
||||||
|
static constexpr auto value =
|
||||||
|
std::is_constructible<typename RealType::key_type, typename CompatibleObjectType::key_type>::value and
|
||||||
|
std::is_constructible<typename RealType::mapped_type, typename CompatibleObjectType::mapped_type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class BasicJsonType, class CompatibleObjectType>
|
||||||
|
struct is_compatible_object_type
|
||||||
|
{
|
||||||
|
static auto constexpr value = is_compatible_object_type_impl <
|
||||||
|
conjunction<negation<std::is_same<void, CompatibleObjectType>>,
|
||||||
|
has_mapped_type<CompatibleObjectType>,
|
||||||
|
has_key_type<CompatibleObjectType>>::value,
|
||||||
|
typename BasicJsonType::object_t, CompatibleObjectType >::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
struct is_basic_json_nested_type
|
||||||
|
{
|
||||||
|
static auto constexpr value = std::is_same<T, typename BasicJsonType::iterator>::value or
|
||||||
|
std::is_same<T, typename BasicJsonType::const_iterator>::value or
|
||||||
|
std::is_same<T, typename BasicJsonType::reverse_iterator>::value or
|
||||||
|
std::is_same<T, typename BasicJsonType::const_reverse_iterator>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class BasicJsonType, class CompatibleArrayType>
|
||||||
|
struct is_compatible_array_type
|
||||||
|
{
|
||||||
|
static auto constexpr value =
|
||||||
|
conjunction<negation<std::is_same<void, CompatibleArrayType>>,
|
||||||
|
negation<is_compatible_object_type<
|
||||||
|
BasicJsonType, CompatibleArrayType>>,
|
||||||
|
negation<std::is_constructible<typename BasicJsonType::string_t,
|
||||||
|
CompatibleArrayType>>,
|
||||||
|
negation<is_basic_json_nested_type<BasicJsonType, CompatibleArrayType>>,
|
||||||
|
has_value_type<CompatibleArrayType>,
|
||||||
|
has_iterator<CompatibleArrayType>>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<bool, typename, typename>
|
||||||
|
struct is_compatible_integer_type_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
||||||
|
struct is_compatible_integer_type_impl<true, RealIntegerType, CompatibleNumberIntegerType>
|
||||||
|
{
|
||||||
|
// is there an assert somewhere on overflows?
|
||||||
|
using RealLimits = std::numeric_limits<RealIntegerType>;
|
||||||
|
using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
|
||||||
|
|
||||||
|
static constexpr auto value =
|
||||||
|
std::is_constructible<RealIntegerType, CompatibleNumberIntegerType>::value and
|
||||||
|
CompatibleLimits::is_integer and
|
||||||
|
RealLimits::is_signed == CompatibleLimits::is_signed;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename RealIntegerType, typename CompatibleNumberIntegerType>
|
||||||
|
struct is_compatible_integer_type
|
||||||
|
{
|
||||||
|
static constexpr auto value =
|
||||||
|
is_compatible_integer_type_impl <
|
||||||
|
std::is_integral<CompatibleNumberIntegerType>::value and
|
||||||
|
not std::is_same<bool, CompatibleNumberIntegerType>::value,
|
||||||
|
RealIntegerType, CompatibleNumberIntegerType > ::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
struct has_from_json
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// also check the return type of from_json
|
||||||
|
template<typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
|
||||||
|
std::declval<BasicJsonType>(), std::declval<T&>()))>::value>>
|
||||||
|
static int detect(U&&);
|
||||||
|
static void detect(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool value = std::is_integral<decltype(
|
||||||
|
detect(std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This trait checks if JSONSerializer<T>::from_json(json const&) exists
|
||||||
|
// this overload is used for non-default-constructible user-defined-types
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
struct has_non_default_from_json
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template <
|
||||||
|
typename U,
|
||||||
|
typename = enable_if_t<std::is_same<
|
||||||
|
T, decltype(uncvref_t<U>::from_json(std::declval<BasicJsonType>()))>::value >>
|
||||||
|
static int detect(U&&);
|
||||||
|
static void detect(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool value = std::is_integral<decltype(detect(
|
||||||
|
std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
struct has_to_json
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template<typename U, typename = decltype(uncvref_t<U>::to_json(
|
||||||
|
std::declval<BasicJsonType&>(), std::declval<T>()))>
|
||||||
|
static int detect(U&&);
|
||||||
|
static void detect(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool value = std::is_integral<decltype(detect(
|
||||||
|
std::declval<typename BasicJsonType::template json_serializer<T, void>>()))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// taken from ranges-v3
|
||||||
|
template<typename T>
|
||||||
|
struct static_const
|
||||||
|
{
|
||||||
|
static constexpr T value{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T static_const<T>::value;
|
||||||
|
}
|
||||||
|
}
|
1090
develop/detail/parsing/binary_reader.hpp
Normal file
1090
develop/detail/parsing/binary_reader.hpp
Normal file
File diff suppressed because it is too large
Load diff
555
develop/detail/parsing/binary_writer.hpp
Normal file
555
develop/detail/parsing/binary_writer.hpp
Normal file
|
@ -0,0 +1,555 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // reverse
|
||||||
|
#include <array> // array
|
||||||
|
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
|
||||||
|
#include <cstring> // memcpy
|
||||||
|
#include <limits> // numeric_limits
|
||||||
|
|
||||||
|
#include "detail/parsing/binary_reader.hpp"
|
||||||
|
#include "detail/parsing/output_adapters.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
///////////////////
|
||||||
|
// binary writer //
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief serialization to CBOR and MessagePack values
|
||||||
|
*/
|
||||||
|
template<typename BasicJsonType, typename CharType>
|
||||||
|
class binary_writer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
@brief create a binary writer
|
||||||
|
|
||||||
|
@param[in] adapter output adapter to write to
|
||||||
|
*/
|
||||||
|
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)
|
||||||
|
{
|
||||||
|
assert(oa);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief[in] j JSON value to serialize
|
||||||
|
*/
|
||||||
|
void write_cbor(const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
switch (j.type())
|
||||||
|
{
|
||||||
|
case value_t::null:
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xF6));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::boolean:
|
||||||
|
{
|
||||||
|
oa->write_character(j.m_value.boolean
|
||||||
|
? static_cast<CharType>(0xF5)
|
||||||
|
: static_cast<CharType>(0xF4));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_integer:
|
||||||
|
{
|
||||||
|
if (j.m_value.number_integer >= 0)
|
||||||
|
{
|
||||||
|
// CBOR does not differentiate between positive signed
|
||||||
|
// integers and unsigned integers. Therefore, we used the
|
||||||
|
// code from the value_t::number_unsigned case here.
|
||||||
|
if (j.m_value.number_integer <= 0x17)
|
||||||
|
{
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x18));
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x19));
|
||||||
|
write_number(static_cast<uint16_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x1A));
|
||||||
|
write_number(static_cast<uint32_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x1B));
|
||||||
|
write_number(static_cast<uint64_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The conversions below encode the sign in the first
|
||||||
|
// byte, and the value is converted to a positive number.
|
||||||
|
const auto positive_number = -1 - j.m_value.number_integer;
|
||||||
|
if (j.m_value.number_integer >= -24)
|
||||||
|
{
|
||||||
|
write_number(static_cast<uint8_t>(0x20 + positive_number));
|
||||||
|
}
|
||||||
|
else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x38));
|
||||||
|
write_number(static_cast<uint8_t>(positive_number));
|
||||||
|
}
|
||||||
|
else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x39));
|
||||||
|
write_number(static_cast<uint16_t>(positive_number));
|
||||||
|
}
|
||||||
|
else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x3A));
|
||||||
|
write_number(static_cast<uint32_t>(positive_number));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x3B));
|
||||||
|
write_number(static_cast<uint64_t>(positive_number));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
{
|
||||||
|
if (j.m_value.number_unsigned <= 0x17)
|
||||||
|
{
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x18));
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x19));
|
||||||
|
write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x1A));
|
||||||
|
write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x1B));
|
||||||
|
write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_float: // Double-Precision Float
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xFB));
|
||||||
|
write_number(j.m_value.number_float);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::string:
|
||||||
|
{
|
||||||
|
// step 1: write control byte and the string length
|
||||||
|
const auto N = j.m_value.string->size();
|
||||||
|
if (N <= 0x17)
|
||||||
|
{
|
||||||
|
write_number(static_cast<uint8_t>(0x60 + N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x78));
|
||||||
|
write_number(static_cast<uint8_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x79));
|
||||||
|
write_number(static_cast<uint16_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x7A));
|
||||||
|
write_number(static_cast<uint32_t>(N));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
else if (N <= 0xFFFFFFFFFFFFFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x7B));
|
||||||
|
write_number(static_cast<uint64_t>(N));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
|
// step 2: write the string
|
||||||
|
oa->write_characters(
|
||||||
|
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
|
||||||
|
j.m_value.string->size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
// step 1: write control byte and the array size
|
||||||
|
const auto N = j.m_value.array->size();
|
||||||
|
if (N <= 0x17)
|
||||||
|
{
|
||||||
|
write_number(static_cast<uint8_t>(0x80 + N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x98));
|
||||||
|
write_number(static_cast<uint8_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x99));
|
||||||
|
write_number(static_cast<uint16_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x9A));
|
||||||
|
write_number(static_cast<uint32_t>(N));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
else if (N <= 0xFFFFFFFFFFFFFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0x9B));
|
||||||
|
write_number(static_cast<uint64_t>(N));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
|
// step 2: write each element
|
||||||
|
for (const auto& el : *j.m_value.array)
|
||||||
|
{
|
||||||
|
write_cbor(el);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
// step 1: write control byte and the object size
|
||||||
|
const auto N = j.m_value.object->size();
|
||||||
|
if (N <= 0x17)
|
||||||
|
{
|
||||||
|
write_number(static_cast<uint8_t>(0xA0 + N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xB8));
|
||||||
|
write_number(static_cast<uint8_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xB9));
|
||||||
|
write_number(static_cast<uint16_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xBA));
|
||||||
|
write_number(static_cast<uint32_t>(N));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
else if (N <= 0xFFFFFFFFFFFFFFFF)
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xBB));
|
||||||
|
write_number(static_cast<uint64_t>(N));
|
||||||
|
}
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
|
// step 2: write each element
|
||||||
|
for (const auto& el : *j.m_value.object)
|
||||||
|
{
|
||||||
|
write_cbor(el.first);
|
||||||
|
write_cbor(el.second);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief[in] j JSON value to serialize
|
||||||
|
*/
|
||||||
|
void write_msgpack(const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
switch (j.type())
|
||||||
|
{
|
||||||
|
case value_t::null: // nil
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xC0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::boolean: // true and false
|
||||||
|
{
|
||||||
|
oa->write_character(j.m_value.boolean
|
||||||
|
? static_cast<CharType>(0xC3)
|
||||||
|
: static_cast<CharType>(0xC2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_integer:
|
||||||
|
{
|
||||||
|
if (j.m_value.number_integer >= 0)
|
||||||
|
{
|
||||||
|
// MessagePack does not differentiate between positive
|
||||||
|
// signed integers and unsigned integers. Therefore, we used
|
||||||
|
// the code from the value_t::number_unsigned case here.
|
||||||
|
if (j.m_value.number_unsigned < 128)
|
||||||
|
{
|
||||||
|
// positive fixnum
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 8
|
||||||
|
oa->write_character(static_cast<CharType>(0xCC));
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 16
|
||||||
|
oa->write_character(static_cast<CharType>(0xCD));
|
||||||
|
write_number(static_cast<uint16_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 32
|
||||||
|
oa->write_character(static_cast<CharType>(0xCE));
|
||||||
|
write_number(static_cast<uint32_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 64
|
||||||
|
oa->write_character(static_cast<CharType>(0xCF));
|
||||||
|
write_number(static_cast<uint64_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (j.m_value.number_integer >= -32)
|
||||||
|
{
|
||||||
|
// negative fixnum
|
||||||
|
write_number(static_cast<int8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and
|
||||||
|
j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
|
||||||
|
{
|
||||||
|
// int 8
|
||||||
|
oa->write_character(static_cast<CharType>(0xD0));
|
||||||
|
write_number(static_cast<int8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and
|
||||||
|
j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
|
||||||
|
{
|
||||||
|
// int 16
|
||||||
|
oa->write_character(static_cast<CharType>(0xD1));
|
||||||
|
write_number(static_cast<int16_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and
|
||||||
|
j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
|
||||||
|
{
|
||||||
|
// int 32
|
||||||
|
oa->write_character(static_cast<CharType>(0xD2));
|
||||||
|
write_number(static_cast<int32_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and
|
||||||
|
j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
|
||||||
|
{
|
||||||
|
// int 64
|
||||||
|
oa->write_character(static_cast<CharType>(0xD3));
|
||||||
|
write_number(static_cast<int64_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
{
|
||||||
|
if (j.m_value.number_unsigned < 128)
|
||||||
|
{
|
||||||
|
// positive fixnum
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 8
|
||||||
|
oa->write_character(static_cast<CharType>(0xCC));
|
||||||
|
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 16
|
||||||
|
oa->write_character(static_cast<CharType>(0xCD));
|
||||||
|
write_number(static_cast<uint16_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 32
|
||||||
|
oa->write_character(static_cast<CharType>(0xCE));
|
||||||
|
write_number(static_cast<uint32_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
|
||||||
|
{
|
||||||
|
// uint 64
|
||||||
|
oa->write_character(static_cast<CharType>(0xCF));
|
||||||
|
write_number(static_cast<uint64_t>(j.m_value.number_integer));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_float: // float 64
|
||||||
|
{
|
||||||
|
oa->write_character(static_cast<CharType>(0xCB));
|
||||||
|
write_number(j.m_value.number_float);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::string:
|
||||||
|
{
|
||||||
|
// step 1: write control byte and the string length
|
||||||
|
const auto N = j.m_value.string->size();
|
||||||
|
if (N <= 31)
|
||||||
|
{
|
||||||
|
// fixstr
|
||||||
|
write_number(static_cast<uint8_t>(0xA0 | N));
|
||||||
|
}
|
||||||
|
else if (N <= 255)
|
||||||
|
{
|
||||||
|
// str 8
|
||||||
|
oa->write_character(static_cast<CharType>(0xD9));
|
||||||
|
write_number(static_cast<uint8_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 65535)
|
||||||
|
{
|
||||||
|
// str 16
|
||||||
|
oa->write_character(static_cast<CharType>(0xDA));
|
||||||
|
write_number(static_cast<uint16_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 4294967295)
|
||||||
|
{
|
||||||
|
// str 32
|
||||||
|
oa->write_character(static_cast<CharType>(0xDB));
|
||||||
|
write_number(static_cast<uint32_t>(N));
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 2: write the string
|
||||||
|
oa->write_characters(
|
||||||
|
reinterpret_cast<const CharType*>(j.m_value.string->c_str()),
|
||||||
|
j.m_value.string->size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
// step 1: write control byte and the array size
|
||||||
|
const auto N = j.m_value.array->size();
|
||||||
|
if (N <= 15)
|
||||||
|
{
|
||||||
|
// fixarray
|
||||||
|
write_number(static_cast<uint8_t>(0x90 | N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFF)
|
||||||
|
{
|
||||||
|
// array 16
|
||||||
|
oa->write_character(static_cast<CharType>(0xDC));
|
||||||
|
write_number(static_cast<uint16_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 0xFFFFFFFF)
|
||||||
|
{
|
||||||
|
// array 32
|
||||||
|
oa->write_character(static_cast<CharType>(0xDD));
|
||||||
|
write_number(static_cast<uint32_t>(N));
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 2: write each element
|
||||||
|
for (const auto& el : *j.m_value.array)
|
||||||
|
{
|
||||||
|
write_msgpack(el);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
// step 1: write control byte and the object size
|
||||||
|
const auto N = j.m_value.object->size();
|
||||||
|
if (N <= 15)
|
||||||
|
{
|
||||||
|
// fixmap
|
||||||
|
write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));
|
||||||
|
}
|
||||||
|
else if (N <= 65535)
|
||||||
|
{
|
||||||
|
// map 16
|
||||||
|
oa->write_character(static_cast<CharType>(0xDE));
|
||||||
|
write_number(static_cast<uint16_t>(N));
|
||||||
|
}
|
||||||
|
else if (N <= 4294967295)
|
||||||
|
{
|
||||||
|
// map 32
|
||||||
|
oa->write_character(static_cast<CharType>(0xDF));
|
||||||
|
write_number(static_cast<uint32_t>(N));
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 2: write each element
|
||||||
|
for (const auto& el : *j.m_value.object)
|
||||||
|
{
|
||||||
|
write_msgpack(el.first);
|
||||||
|
write_msgpack(el.second);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
@brief write a number to output input
|
||||||
|
|
||||||
|
@param[in] n number of type @a NumberType
|
||||||
|
@tparam NumberType the type of the number
|
||||||
|
|
||||||
|
@note This function needs to respect the system's endianess, because bytes
|
||||||
|
in CBOR and MessagePack are stored in network order (big endian) and
|
||||||
|
therefore need reordering on little endian systems.
|
||||||
|
*/
|
||||||
|
template<typename NumberType> void write_number(NumberType n)
|
||||||
|
{
|
||||||
|
// step 1: write number to array of length NumberType
|
||||||
|
std::array<CharType, sizeof(NumberType)> vec;
|
||||||
|
std::memcpy(vec.data(), &n, sizeof(NumberType));
|
||||||
|
|
||||||
|
// step 2: write array to output (with possible reordering)
|
||||||
|
if (is_little_endian)
|
||||||
|
{
|
||||||
|
// reverse byte order prior to conversion if necessary
|
||||||
|
std::reverse(vec.begin(), vec.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
oa->write_characters(vec.data(), sizeof(NumberType));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// whether we can assume little endianess
|
||||||
|
const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();
|
||||||
|
|
||||||
|
/// the output
|
||||||
|
output_adapter_t<CharType> oa = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
262
develop/detail/parsing/input_adapters.hpp
Normal file
262
develop/detail/parsing/input_adapters.hpp
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // min
|
||||||
|
#include <array> // array
|
||||||
|
#include <cassert> // assert
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <cstring> // strlen
|
||||||
|
#include <ios> // streamsize, streamoff, streampos
|
||||||
|
#include <istream> // istream
|
||||||
|
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
|
||||||
|
#include <memory> // shared_ptr, make_shared, addressof
|
||||||
|
#include <numeric> // accumulate
|
||||||
|
#include <string> // string, char_traits
|
||||||
|
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
||||||
|
#include <utility> // pair, declval
|
||||||
|
|
||||||
|
#include "detail/macro_scope.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
////////////////////
|
||||||
|
// input adapters //
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief abstract input adapter interface
|
||||||
|
|
||||||
|
Produces a stream of std::char_traits<char>::int_type characters from a
|
||||||
|
std::istream, a buffer, or some other input type. Accepts the return of exactly
|
||||||
|
one non-EOF character for future input. The int_type characters returned
|
||||||
|
consist of all valid char values as positive values (typically unsigned char),
|
||||||
|
plus an EOF value outside that range, specified by the value of the function
|
||||||
|
std::char_traits<char>::eof(). This value is typically -1, but could be any
|
||||||
|
arbitrary value which is not a valid char value.
|
||||||
|
*/
|
||||||
|
struct input_adapter_protocol
|
||||||
|
{
|
||||||
|
/// get a character [0,255] or std::char_traits<char>::eof().
|
||||||
|
virtual std::char_traits<char>::int_type get_character() = 0;
|
||||||
|
/// restore the last non-eof() character to input
|
||||||
|
virtual void unget_character() = 0;
|
||||||
|
virtual ~input_adapter_protocol() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// a type to simplify interfaces
|
||||||
|
using input_adapter_t = std::shared_ptr<input_adapter_protocol>;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
|
||||||
|
beginning of input. Does not support changing the underlying std::streambuf
|
||||||
|
in mid-input. Maintains underlying std::istream and std::streambuf to support
|
||||||
|
subsequent use of standard std::istream operations to process any input
|
||||||
|
characters following those used in parsing the JSON input. Clears the
|
||||||
|
std::istream flags; any input errors (e.g., EOF) will be detected by the first
|
||||||
|
subsequent call for input from the std::istream.
|
||||||
|
*/
|
||||||
|
class input_stream_adapter : public input_adapter_protocol
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~input_stream_adapter() override
|
||||||
|
{
|
||||||
|
// clear stream flags; we use underlying streambuf I/O, do not
|
||||||
|
// maintain ifstream flags
|
||||||
|
is.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit input_stream_adapter(std::istream& i)
|
||||||
|
: is(i), sb(*i.rdbuf())
|
||||||
|
{
|
||||||
|
// skip byte order mark
|
||||||
|
std::char_traits<char>::int_type c;
|
||||||
|
if ((c = get_character()) == 0xEF)
|
||||||
|
{
|
||||||
|
if ((c = get_character()) == 0xBB)
|
||||||
|
{
|
||||||
|
if ((c = get_character()) == 0xBF)
|
||||||
|
{
|
||||||
|
return; // Ignore BOM
|
||||||
|
}
|
||||||
|
else if (c != std::char_traits<char>::eof())
|
||||||
|
{
|
||||||
|
is.unget();
|
||||||
|
}
|
||||||
|
is.putback('\xBB');
|
||||||
|
}
|
||||||
|
else if (c != std::char_traits<char>::eof())
|
||||||
|
{
|
||||||
|
is.unget();
|
||||||
|
}
|
||||||
|
is.putback('\xEF');
|
||||||
|
}
|
||||||
|
else if (c != std::char_traits<char>::eof())
|
||||||
|
{
|
||||||
|
is.unget(); // no byte order mark; process as usual
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete because of pointer members
|
||||||
|
input_stream_adapter(const input_stream_adapter&) = delete;
|
||||||
|
input_stream_adapter& operator=(input_stream_adapter&) = delete;
|
||||||
|
|
||||||
|
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
|
||||||
|
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
|
||||||
|
// end up as the same value, eg. 0xFFFFFFFF.
|
||||||
|
std::char_traits<char>::int_type get_character() override
|
||||||
|
{
|
||||||
|
return sb.sbumpc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unget_character() override
|
||||||
|
{
|
||||||
|
sb.sungetc(); // is.unget() avoided for performance
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// the associated input stream
|
||||||
|
std::istream& is;
|
||||||
|
std::streambuf& sb;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// input adapter for buffer input
|
||||||
|
class input_buffer_adapter : public input_adapter_protocol
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
input_buffer_adapter(const char* b, const std::size_t l)
|
||||||
|
: cursor(b), limit(b + l), start(b)
|
||||||
|
{
|
||||||
|
// skip byte order mark
|
||||||
|
if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF')
|
||||||
|
{
|
||||||
|
cursor += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete because of pointer members
|
||||||
|
input_buffer_adapter(const input_buffer_adapter&) = delete;
|
||||||
|
input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
|
||||||
|
|
||||||
|
std::char_traits<char>::int_type get_character() noexcept override
|
||||||
|
{
|
||||||
|
if (JSON_LIKELY(cursor < limit))
|
||||||
|
{
|
||||||
|
return std::char_traits<char>::to_int_type(*(cursor++));
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::char_traits<char>::eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unget_character() noexcept override
|
||||||
|
{
|
||||||
|
if (JSON_LIKELY(cursor > start))
|
||||||
|
{
|
||||||
|
--cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// pointer to the current character
|
||||||
|
const char* cursor;
|
||||||
|
/// pointer past the last character
|
||||||
|
const char* limit;
|
||||||
|
/// pointer to the first character
|
||||||
|
const char* start;
|
||||||
|
};
|
||||||
|
|
||||||
|
class input_adapter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// native support
|
||||||
|
|
||||||
|
/// input adapter for input stream
|
||||||
|
input_adapter(std::istream& i)
|
||||||
|
: ia(std::make_shared<input_stream_adapter>(i)) {}
|
||||||
|
|
||||||
|
/// input adapter for input stream
|
||||||
|
input_adapter(std::istream&& i)
|
||||||
|
: ia(std::make_shared<input_stream_adapter>(i)) {}
|
||||||
|
|
||||||
|
/// input adapter for buffer
|
||||||
|
template<typename CharT,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_pointer<CharT>::value and
|
||||||
|
std::is_integral<typename std::remove_pointer<CharT>::type>::value and
|
||||||
|
sizeof(typename std::remove_pointer<CharT>::type) == 1,
|
||||||
|
int>::type = 0>
|
||||||
|
input_adapter(CharT b, std::size_t l)
|
||||||
|
: ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}
|
||||||
|
|
||||||
|
// derived support
|
||||||
|
|
||||||
|
/// input adapter for string literal
|
||||||
|
template<typename CharT,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_pointer<CharT>::value and
|
||||||
|
std::is_integral<typename std::remove_pointer<CharT>::type>::value and
|
||||||
|
sizeof(typename std::remove_pointer<CharT>::type) == 1,
|
||||||
|
int>::type = 0>
|
||||||
|
input_adapter(CharT b)
|
||||||
|
: input_adapter(reinterpret_cast<const char*>(b),
|
||||||
|
std::strlen(reinterpret_cast<const char*>(b))) {}
|
||||||
|
|
||||||
|
/// input adapter for iterator range with contiguous storage
|
||||||
|
template<class IteratorType,
|
||||||
|
typename std::enable_if<
|
||||||
|
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
input_adapter(IteratorType first, IteratorType last)
|
||||||
|
{
|
||||||
|
// assertion to check that the iterator range is indeed contiguous,
|
||||||
|
// see http://stackoverflow.com/a/35008842/266378 for more discussion
|
||||||
|
assert(std::accumulate(
|
||||||
|
first, last, std::pair<bool, int>(true, 0),
|
||||||
|
[&first](std::pair<bool, int> res, decltype(*first) val)
|
||||||
|
{
|
||||||
|
res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
|
||||||
|
return res;
|
||||||
|
}).first);
|
||||||
|
|
||||||
|
// assertion to check that each element is 1 byte long
|
||||||
|
static_assert(
|
||||||
|
sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
|
||||||
|
"each element in the iterator range must have the size of 1 byte");
|
||||||
|
|
||||||
|
const auto len = static_cast<size_t>(std::distance(first, last));
|
||||||
|
if (JSON_LIKELY(len > 0))
|
||||||
|
{
|
||||||
|
// there is at least one element: use the address of first
|
||||||
|
ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the address of first cannot be used: use nullptr
|
||||||
|
ia = std::make_shared<input_buffer_adapter>(nullptr, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// input adapter for array
|
||||||
|
template<class T, std::size_t N>
|
||||||
|
input_adapter(T (&array)[N])
|
||||||
|
: input_adapter(std::begin(array), std::end(array)) {}
|
||||||
|
|
||||||
|
/// input adapter for contiguous container
|
||||||
|
template<class ContiguousContainer, typename
|
||||||
|
std::enable_if<not std::is_pointer<ContiguousContainer>::value and
|
||||||
|
std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
|
||||||
|
int>::type = 0>
|
||||||
|
input_adapter(const ContiguousContainer& c)
|
||||||
|
: input_adapter(std::begin(c), std::end(c)) {}
|
||||||
|
|
||||||
|
operator input_adapter_t()
|
||||||
|
{
|
||||||
|
return ia;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// the actual adapter
|
||||||
|
input_adapter_t ia = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
1276
develop/detail/parsing/lexer.hpp
Normal file
1276
develop/detail/parsing/lexer.hpp
Normal file
File diff suppressed because it is too large
Load diff
113
develop/detail/parsing/output_adapters.hpp
Normal file
113
develop/detail/parsing/output_adapters.hpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // copy
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <ios> // streamsize
|
||||||
|
#include <iterator> // back_inserter
|
||||||
|
#include <memory> // shared_ptr, make_shared
|
||||||
|
#include <ostream> // basic_ostream
|
||||||
|
#include <string> // basic_string
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/// abstract output adapter interface
|
||||||
|
template<typename CharType> struct output_adapter_protocol
|
||||||
|
{
|
||||||
|
virtual void write_character(CharType c) = 0;
|
||||||
|
virtual void write_characters(const CharType* s, std::size_t length) = 0;
|
||||||
|
virtual ~output_adapter_protocol() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// a type to simplify interfaces
|
||||||
|
template<typename CharType>
|
||||||
|
using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
|
||||||
|
|
||||||
|
/// output adapter for byte vectors
|
||||||
|
template<typename CharType>
|
||||||
|
class output_vector_adapter : public output_adapter_protocol<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit output_vector_adapter(std::vector<CharType>& vec) : v(vec) {}
|
||||||
|
|
||||||
|
void write_character(CharType c) override
|
||||||
|
{
|
||||||
|
v.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
|
{
|
||||||
|
std::copy(s, s + length, std::back_inserter(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<CharType>& v;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// output adapter for output streams
|
||||||
|
template<typename CharType>
|
||||||
|
class output_stream_adapter : public output_adapter_protocol<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit output_stream_adapter(std::basic_ostream<CharType>& s) : stream(s) {}
|
||||||
|
|
||||||
|
void write_character(CharType c) override
|
||||||
|
{
|
||||||
|
stream.put(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
|
{
|
||||||
|
stream.write(s, static_cast<std::streamsize>(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::basic_ostream<CharType>& stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// output adapter for basic_string
|
||||||
|
template<typename CharType>
|
||||||
|
class output_string_adapter : public output_adapter_protocol<CharType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit output_string_adapter(std::basic_string<CharType>& s) : str(s) {}
|
||||||
|
|
||||||
|
void write_character(CharType c) override
|
||||||
|
{
|
||||||
|
str.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_characters(const CharType* s, std::size_t length) override
|
||||||
|
{
|
||||||
|
str.append(s, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::basic_string<CharType>& str;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename CharType>
|
||||||
|
class output_adapter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
output_adapter(std::vector<CharType>& vec)
|
||||||
|
: oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}
|
||||||
|
|
||||||
|
output_adapter(std::basic_ostream<CharType>& s)
|
||||||
|
: oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
|
||||||
|
|
||||||
|
output_adapter(std::basic_string<CharType>& s)
|
||||||
|
: oa(std::make_shared<output_string_adapter<CharType>>(s)) {}
|
||||||
|
|
||||||
|
operator output_adapter_t<CharType>()
|
||||||
|
{
|
||||||
|
return oa;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
output_adapter_t<CharType> oa = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
589
develop/detail/parsing/parser.hpp
Normal file
589
develop/detail/parsing/parser.hpp
Normal file
|
@ -0,0 +1,589 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert> // assert
|
||||||
|
#include <cmath> // isfinite
|
||||||
|
#include <cstdint> // uint8_t
|
||||||
|
#include <functional> // function
|
||||||
|
#include <string> // string
|
||||||
|
#include <utility> // move
|
||||||
|
|
||||||
|
#include "detail/exceptions.hpp"
|
||||||
|
#include "detail/macro_scope.hpp"
|
||||||
|
#include "detail/parsing/input_adapters.hpp"
|
||||||
|
#include "detail/parsing/lexer.hpp"
|
||||||
|
#include "detail/value_t.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
////////////
|
||||||
|
// parser //
|
||||||
|
////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief syntax analysis
|
||||||
|
|
||||||
|
This class implements a recursive decent parser.
|
||||||
|
*/
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
class parser
|
||||||
|
{
|
||||||
|
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||||
|
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||||
|
using number_float_t = typename BasicJsonType::number_float_t;
|
||||||
|
using lexer_t = lexer<BasicJsonType>;
|
||||||
|
using token_type = typename lexer_t::token_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class parse_event_t : uint8_t
|
||||||
|
{
|
||||||
|
/// the parser read `{` and started to process a JSON object
|
||||||
|
object_start,
|
||||||
|
/// the parser read `}` and finished processing a JSON object
|
||||||
|
object_end,
|
||||||
|
/// the parser read `[` and started to process a JSON array
|
||||||
|
array_start,
|
||||||
|
/// the parser read `]` and finished processing a JSON array
|
||||||
|
array_end,
|
||||||
|
/// the parser read a key of a value in an object
|
||||||
|
key,
|
||||||
|
/// the parser finished reading a JSON value
|
||||||
|
value
|
||||||
|
};
|
||||||
|
|
||||||
|
using parser_callback_t =
|
||||||
|
std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
|
||||||
|
|
||||||
|
/// a parser reading from an input adapter
|
||||||
|
explicit parser(detail::input_adapter_t adapter,
|
||||||
|
const parser_callback_t cb = nullptr,
|
||||||
|
const bool allow_exceptions_ = true)
|
||||||
|
: callback(cb), m_lexer(adapter), allow_exceptions(allow_exceptions_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief public parser interface
|
||||||
|
|
||||||
|
@param[in] strict whether to expect the last token to be EOF
|
||||||
|
@param[in,out] result parsed JSON value
|
||||||
|
|
||||||
|
@throw parse_error.101 in case of an unexpected token
|
||||||
|
@throw parse_error.102 if to_unicode fails or surrogate error
|
||||||
|
@throw parse_error.103 if to_unicode fails
|
||||||
|
*/
|
||||||
|
void parse(const bool strict, BasicJsonType& result)
|
||||||
|
{
|
||||||
|
// read first token
|
||||||
|
get_token();
|
||||||
|
|
||||||
|
parse_internal(true, result);
|
||||||
|
result.assert_invariant();
|
||||||
|
|
||||||
|
// in strict mode, input must be completely read
|
||||||
|
if (strict)
|
||||||
|
{
|
||||||
|
get_token();
|
||||||
|
expect(token_type::end_of_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case of an error, return discarded value
|
||||||
|
if (errored)
|
||||||
|
{
|
||||||
|
result = value_t::discarded;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set top-level value to null if it was discarded by the callback
|
||||||
|
// function
|
||||||
|
if (result.is_discarded())
|
||||||
|
{
|
||||||
|
result = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief public accept interface
|
||||||
|
|
||||||
|
@param[in] strict whether to expect the last token to be EOF
|
||||||
|
@return whether the input is a proper JSON text
|
||||||
|
*/
|
||||||
|
bool accept(const bool strict = true)
|
||||||
|
{
|
||||||
|
// read first token
|
||||||
|
get_token();
|
||||||
|
|
||||||
|
if (not accept_internal())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// strict => last token must be EOF
|
||||||
|
return not strict or (get_token() == token_type::end_of_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
@brief the actual parser
|
||||||
|
@throw parse_error.101 in case of an unexpected token
|
||||||
|
@throw parse_error.102 if to_unicode fails or surrogate error
|
||||||
|
@throw parse_error.103 if to_unicode fails
|
||||||
|
*/
|
||||||
|
void parse_internal(bool keep, BasicJsonType& result)
|
||||||
|
{
|
||||||
|
// never parse after a parse error was detected
|
||||||
|
assert(not errored);
|
||||||
|
|
||||||
|
// start with a discarded value
|
||||||
|
if (not result.is_discarded())
|
||||||
|
{
|
||||||
|
result.m_value.destroy(result.m_type);
|
||||||
|
result.m_type = value_t::discarded;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (last_token)
|
||||||
|
{
|
||||||
|
case token_type::begin_object:
|
||||||
|
{
|
||||||
|
if (keep)
|
||||||
|
{
|
||||||
|
if (callback)
|
||||||
|
{
|
||||||
|
keep = callback(depth++, parse_event_t::object_start, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not callback or keep)
|
||||||
|
{
|
||||||
|
// explicitly set result to object to cope with {}
|
||||||
|
result.m_type = value_t::object;
|
||||||
|
result.m_value = value_t::object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read next token
|
||||||
|
get_token();
|
||||||
|
|
||||||
|
// closing } -> we are done
|
||||||
|
if (last_token == token_type::end_object)
|
||||||
|
{
|
||||||
|
if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
|
||||||
|
{
|
||||||
|
result.m_value.destroy(result.m_type);
|
||||||
|
result.m_type = value_t::discarded;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse values
|
||||||
|
std::string key;
|
||||||
|
BasicJsonType value;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// store key
|
||||||
|
if (not expect(token_type::value_string))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
key = m_lexer.move_string();
|
||||||
|
|
||||||
|
bool keep_tag = false;
|
||||||
|
if (keep)
|
||||||
|
{
|
||||||
|
if (callback)
|
||||||
|
{
|
||||||
|
BasicJsonType k(key);
|
||||||
|
keep_tag = callback(depth, parse_event_t::key, k);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keep_tag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse separator (:)
|
||||||
|
get_token();
|
||||||
|
if (not expect(token_type::name_separator))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse and add value
|
||||||
|
get_token();
|
||||||
|
value.m_value.destroy(value.m_type);
|
||||||
|
value.m_type = value_t::discarded;
|
||||||
|
parse_internal(keep, value);
|
||||||
|
|
||||||
|
if (JSON_UNLIKELY(errored))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep and keep_tag and not value.is_discarded())
|
||||||
|
{
|
||||||
|
result.m_value.object->emplace(std::move(key), std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// comma -> next value
|
||||||
|
get_token();
|
||||||
|
if (last_token == token_type::value_separator)
|
||||||
|
{
|
||||||
|
get_token();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// closing }
|
||||||
|
if (not expect(token_type::end_object))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
|
||||||
|
{
|
||||||
|
result.m_value.destroy(result.m_type);
|
||||||
|
result.m_type = value_t::discarded;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::begin_array:
|
||||||
|
{
|
||||||
|
if (keep)
|
||||||
|
{
|
||||||
|
if (callback)
|
||||||
|
{
|
||||||
|
keep = callback(depth++, parse_event_t::array_start, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not callback or keep)
|
||||||
|
{
|
||||||
|
// explicitly set result to array to cope with []
|
||||||
|
result.m_type = value_t::array;
|
||||||
|
result.m_value = value_t::array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read next token
|
||||||
|
get_token();
|
||||||
|
|
||||||
|
// closing ] -> we are done
|
||||||
|
if (last_token == token_type::end_array)
|
||||||
|
{
|
||||||
|
if (callback and not callback(--depth, parse_event_t::array_end, result))
|
||||||
|
{
|
||||||
|
result.m_value.destroy(result.m_type);
|
||||||
|
result.m_type = value_t::discarded;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse values
|
||||||
|
BasicJsonType value;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// parse value
|
||||||
|
value.m_value.destroy(value.m_type);
|
||||||
|
value.m_type = value_t::discarded;
|
||||||
|
parse_internal(keep, value);
|
||||||
|
|
||||||
|
if (JSON_UNLIKELY(errored))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep and not value.is_discarded())
|
||||||
|
{
|
||||||
|
result.m_value.array->push_back(std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// comma -> next value
|
||||||
|
get_token();
|
||||||
|
if (last_token == token_type::value_separator)
|
||||||
|
{
|
||||||
|
get_token();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// closing ]
|
||||||
|
if (not expect(token_type::end_array))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
|
||||||
|
{
|
||||||
|
result.m_value.destroy(result.m_type);
|
||||||
|
result.m_type = value_t::discarded;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::literal_null:
|
||||||
|
{
|
||||||
|
result.m_type = value_t::null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::value_string:
|
||||||
|
{
|
||||||
|
result.m_type = value_t::string;
|
||||||
|
result.m_value = m_lexer.move_string();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::literal_true:
|
||||||
|
{
|
||||||
|
result.m_type = value_t::boolean;
|
||||||
|
result.m_value = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::literal_false:
|
||||||
|
{
|
||||||
|
result.m_type = value_t::boolean;
|
||||||
|
result.m_value = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::value_unsigned:
|
||||||
|
{
|
||||||
|
result.m_type = value_t::number_unsigned;
|
||||||
|
result.m_value = m_lexer.get_number_unsigned();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::value_integer:
|
||||||
|
{
|
||||||
|
result.m_type = value_t::number_integer;
|
||||||
|
result.m_value = m_lexer.get_number_integer();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::value_float:
|
||||||
|
{
|
||||||
|
result.m_type = value_t::number_float;
|
||||||
|
result.m_value = m_lexer.get_number_float();
|
||||||
|
|
||||||
|
// throw in case of infinity or NAN
|
||||||
|
if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float)))
|
||||||
|
{
|
||||||
|
if (allow_exceptions)
|
||||||
|
{
|
||||||
|
JSON_THROW(out_of_range::create(406, "number overflow parsing '" +
|
||||||
|
m_lexer.get_token_string() + "'"));
|
||||||
|
}
|
||||||
|
expect(token_type::uninitialized);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::parse_error:
|
||||||
|
{
|
||||||
|
// using "uninitialized" to avoid "expected" message
|
||||||
|
if (not expect(token_type::uninitialized))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// the last token was unexpected; we expected a value
|
||||||
|
if (not expect(token_type::literal_or_value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keep and callback and not callback(depth, parse_event_t::value, result))
|
||||||
|
{
|
||||||
|
result.m_type = value_t::discarded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the actual acceptor
|
||||||
|
|
||||||
|
@invariant 1. The last token is not yet processed. Therefore, the caller
|
||||||
|
of this function must make sure a token has been read.
|
||||||
|
2. When this function returns, the last token is processed.
|
||||||
|
That is, the last read character was already considered.
|
||||||
|
|
||||||
|
This invariant makes sure that no token needs to be "unput".
|
||||||
|
*/
|
||||||
|
bool accept_internal()
|
||||||
|
{
|
||||||
|
switch (last_token)
|
||||||
|
{
|
||||||
|
case token_type::begin_object:
|
||||||
|
{
|
||||||
|
// read next token
|
||||||
|
get_token();
|
||||||
|
|
||||||
|
// closing } -> we are done
|
||||||
|
if (last_token == token_type::end_object)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse values
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// parse key
|
||||||
|
if (last_token != token_type::value_string)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse separator (:)
|
||||||
|
get_token();
|
||||||
|
if (last_token != token_type::name_separator)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse value
|
||||||
|
get_token();
|
||||||
|
if (not accept_internal())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// comma -> next value
|
||||||
|
get_token();
|
||||||
|
if (last_token == token_type::value_separator)
|
||||||
|
{
|
||||||
|
get_token();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// closing }
|
||||||
|
return (last_token == token_type::end_object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::begin_array:
|
||||||
|
{
|
||||||
|
// read next token
|
||||||
|
get_token();
|
||||||
|
|
||||||
|
// closing ] -> we are done
|
||||||
|
if (last_token == token_type::end_array)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse values
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// parse value
|
||||||
|
if (not accept_internal())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// comma -> next value
|
||||||
|
get_token();
|
||||||
|
if (last_token == token_type::value_separator)
|
||||||
|
{
|
||||||
|
get_token();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// closing ]
|
||||||
|
return (last_token == token_type::end_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::value_float:
|
||||||
|
{
|
||||||
|
// reject infinity or NAN
|
||||||
|
return std::isfinite(m_lexer.get_number_float());
|
||||||
|
}
|
||||||
|
|
||||||
|
case token_type::literal_false:
|
||||||
|
case token_type::literal_null:
|
||||||
|
case token_type::literal_true:
|
||||||
|
case token_type::value_integer:
|
||||||
|
case token_type::value_string:
|
||||||
|
case token_type::value_unsigned:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default: // the last token was unexpected
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get next token from lexer
|
||||||
|
token_type get_token()
|
||||||
|
{
|
||||||
|
return (last_token = m_lexer.scan());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@throw parse_error.101 if expected token did not occur
|
||||||
|
*/
|
||||||
|
bool expect(token_type t)
|
||||||
|
{
|
||||||
|
if (JSON_UNLIKELY(t != last_token))
|
||||||
|
{
|
||||||
|
errored = true;
|
||||||
|
expected = t;
|
||||||
|
if (allow_exceptions)
|
||||||
|
{
|
||||||
|
throw_exception();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void throw_exception() const
|
||||||
|
{
|
||||||
|
std::string error_msg = "syntax error - ";
|
||||||
|
if (last_token == token_type::parse_error)
|
||||||
|
{
|
||||||
|
error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
|
||||||
|
m_lexer.get_token_string() + "'";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected != token_type::uninitialized)
|
||||||
|
{
|
||||||
|
error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// current level of recursion
|
||||||
|
int depth = 0;
|
||||||
|
/// callback function
|
||||||
|
const parser_callback_t callback = nullptr;
|
||||||
|
/// the type of the last read token
|
||||||
|
token_type last_token = token_type::uninitialized;
|
||||||
|
/// the lexer
|
||||||
|
lexer_t m_lexer;
|
||||||
|
/// whether a syntax error occurred
|
||||||
|
bool errored = false;
|
||||||
|
/// possible reason for the syntax error
|
||||||
|
token_type expected = token_type::uninitialized;
|
||||||
|
/// whether to throw exceptions in case of errors
|
||||||
|
const bool allow_exceptions = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
794
develop/detail/serializer.hpp
Normal file
794
develop/detail/serializer.hpp
Normal file
|
@ -0,0 +1,794 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // reverse, remove, fill, find, none_of
|
||||||
|
#include <array> // array
|
||||||
|
#include <cassert> // assert
|
||||||
|
#include <ciso646> // and, or
|
||||||
|
#include <clocale> // localeconv, lconv
|
||||||
|
#include <cmath> // labs, isfinite, isnan, signbit
|
||||||
|
#include <cstddef> // size_t, ptrdiff_t
|
||||||
|
#include <cstdint> // uint8_t
|
||||||
|
#include <cstdio> // snprintf
|
||||||
|
#include <iterator> // next
|
||||||
|
#include <limits> // numeric_limits
|
||||||
|
#include <string> // string
|
||||||
|
#include <type_traits> // is_same
|
||||||
|
|
||||||
|
#include "detail/macro_scope.hpp"
|
||||||
|
#include "detail/meta.hpp"
|
||||||
|
#include "detail/parsing/output_adapters.hpp"
|
||||||
|
#include "detail/value_t.hpp"
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
///////////////////
|
||||||
|
// serialization //
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
class serializer
|
||||||
|
{
|
||||||
|
using string_t = typename BasicJsonType::string_t;
|
||||||
|
using number_float_t = typename BasicJsonType::number_float_t;
|
||||||
|
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||||
|
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
@param[in] s output stream to serialize to
|
||||||
|
@param[in] ichar indentation character to use
|
||||||
|
*/
|
||||||
|
serializer(output_adapter_t<char> s, const char ichar)
|
||||||
|
: o(std::move(s)), loc(std::localeconv()),
|
||||||
|
thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)),
|
||||||
|
decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)),
|
||||||
|
indent_char(ichar), indent_string(512, indent_char) {}
|
||||||
|
|
||||||
|
// delete because of pointer members
|
||||||
|
serializer(const serializer&) = delete;
|
||||||
|
serializer& operator=(const serializer&) = delete;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief internal implementation of the serialization function
|
||||||
|
|
||||||
|
This function is called by the public member function dump and organizes
|
||||||
|
the serialization internally. The indentation level is propagated as
|
||||||
|
additional parameter. In case of arrays and objects, the function is
|
||||||
|
called recursively.
|
||||||
|
|
||||||
|
- strings and object keys are escaped using `escape_string()`
|
||||||
|
- integer numbers are converted implicitly via `operator<<`
|
||||||
|
- floating-point numbers are converted to a string using `"%g"` format
|
||||||
|
|
||||||
|
@param[in] val value to serialize
|
||||||
|
@param[in] pretty_print whether the output shall be pretty-printed
|
||||||
|
@param[in] indent_step the indent level
|
||||||
|
@param[in] current_indent the current indent level (only used internally)
|
||||||
|
*/
|
||||||
|
void dump(const BasicJsonType& val, const bool pretty_print,
|
||||||
|
const bool ensure_ascii,
|
||||||
|
const unsigned int indent_step,
|
||||||
|
const unsigned int current_indent = 0)
|
||||||
|
{
|
||||||
|
switch (val.m_type)
|
||||||
|
{
|
||||||
|
case value_t::object:
|
||||||
|
{
|
||||||
|
if (val.m_value.object->empty())
|
||||||
|
{
|
||||||
|
o->write_characters("{}", 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pretty_print)
|
||||||
|
{
|
||||||
|
o->write_characters("{\n", 2);
|
||||||
|
|
||||||
|
// variable to hold indentation for recursive calls
|
||||||
|
const auto new_indent = current_indent + indent_step;
|
||||||
|
if (JSON_UNLIKELY(indent_string.size() < new_indent))
|
||||||
|
{
|
||||||
|
indent_string.resize(indent_string.size() * 2, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
auto i = val.m_value.object->cbegin();
|
||||||
|
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
||||||
|
{
|
||||||
|
o->write_characters(indent_string.c_str(), new_indent);
|
||||||
|
o->write_character('\"');
|
||||||
|
dump_escaped(i->first, ensure_ascii);
|
||||||
|
o->write_characters("\": ", 3);
|
||||||
|
dump(i->second, true, ensure_ascii, indent_step, new_indent);
|
||||||
|
o->write_characters(",\n", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// last element
|
||||||
|
assert(i != val.m_value.object->cend());
|
||||||
|
assert(std::next(i) == val.m_value.object->cend());
|
||||||
|
o->write_characters(indent_string.c_str(), new_indent);
|
||||||
|
o->write_character('\"');
|
||||||
|
dump_escaped(i->first, ensure_ascii);
|
||||||
|
o->write_characters("\": ", 3);
|
||||||
|
dump(i->second, true, ensure_ascii, indent_step, new_indent);
|
||||||
|
|
||||||
|
o->write_character('\n');
|
||||||
|
o->write_characters(indent_string.c_str(), current_indent);
|
||||||
|
o->write_character('}');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o->write_character('{');
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
auto i = val.m_value.object->cbegin();
|
||||||
|
for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
||||||
|
{
|
||||||
|
o->write_character('\"');
|
||||||
|
dump_escaped(i->first, ensure_ascii);
|
||||||
|
o->write_characters("\":", 2);
|
||||||
|
dump(i->second, false, ensure_ascii, indent_step, current_indent);
|
||||||
|
o->write_character(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// last element
|
||||||
|
assert(i != val.m_value.object->cend());
|
||||||
|
assert(std::next(i) == val.m_value.object->cend());
|
||||||
|
o->write_character('\"');
|
||||||
|
dump_escaped(i->first, ensure_ascii);
|
||||||
|
o->write_characters("\":", 2);
|
||||||
|
dump(i->second, false, ensure_ascii, indent_step, current_indent);
|
||||||
|
|
||||||
|
o->write_character('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array:
|
||||||
|
{
|
||||||
|
if (val.m_value.array->empty())
|
||||||
|
{
|
||||||
|
o->write_characters("[]", 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pretty_print)
|
||||||
|
{
|
||||||
|
o->write_characters("[\n", 2);
|
||||||
|
|
||||||
|
// variable to hold indentation for recursive calls
|
||||||
|
const auto new_indent = current_indent + indent_step;
|
||||||
|
if (JSON_UNLIKELY(indent_string.size() < new_indent))
|
||||||
|
{
|
||||||
|
indent_string.resize(indent_string.size() * 2, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
for (auto i = val.m_value.array->cbegin();
|
||||||
|
i != val.m_value.array->cend() - 1; ++i)
|
||||||
|
{
|
||||||
|
o->write_characters(indent_string.c_str(), new_indent);
|
||||||
|
dump(*i, true, ensure_ascii, indent_step, new_indent);
|
||||||
|
o->write_characters(",\n", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// last element
|
||||||
|
assert(not val.m_value.array->empty());
|
||||||
|
o->write_characters(indent_string.c_str(), new_indent);
|
||||||
|
dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
|
||||||
|
|
||||||
|
o->write_character('\n');
|
||||||
|
o->write_characters(indent_string.c_str(), current_indent);
|
||||||
|
o->write_character(']');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o->write_character('[');
|
||||||
|
|
||||||
|
// first n-1 elements
|
||||||
|
for (auto i = val.m_value.array->cbegin();
|
||||||
|
i != val.m_value.array->cend() - 1; ++i)
|
||||||
|
{
|
||||||
|
dump(*i, false, ensure_ascii, indent_step, current_indent);
|
||||||
|
o->write_character(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// last element
|
||||||
|
assert(not val.m_value.array->empty());
|
||||||
|
dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
|
||||||
|
|
||||||
|
o->write_character(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::string:
|
||||||
|
{
|
||||||
|
o->write_character('\"');
|
||||||
|
dump_escaped(*val.m_value.string, ensure_ascii);
|
||||||
|
o->write_character('\"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::boolean:
|
||||||
|
{
|
||||||
|
if (val.m_value.boolean)
|
||||||
|
{
|
||||||
|
o->write_characters("true", 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o->write_characters("false", 5);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_integer:
|
||||||
|
{
|
||||||
|
dump_integer(val.m_value.number_integer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
{
|
||||||
|
dump_integer(val.m_value.number_unsigned);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::number_float:
|
||||||
|
{
|
||||||
|
dump_float(val.m_value.number_float);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::discarded:
|
||||||
|
{
|
||||||
|
o->write_characters("<discarded>", 11);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::null:
|
||||||
|
{
|
||||||
|
o->write_characters("null", 4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
@brief returns the number of expected bytes following in UTF-8 string
|
||||||
|
|
||||||
|
@param[in] u the first byte of a UTF-8 string
|
||||||
|
@return the number of expected bytes following
|
||||||
|
*/
|
||||||
|
static constexpr std::size_t bytes_following(const uint8_t u)
|
||||||
|
{
|
||||||
|
return ((u <= 127) ? 0
|
||||||
|
: ((192 <= u and u <= 223) ? 1
|
||||||
|
: ((224 <= u and u <= 239) ? 2
|
||||||
|
: ((240 <= u and u <= 247) ? 3 : std::string::npos))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief calculates the extra space to escape a JSON string
|
||||||
|
|
||||||
|
@param[in] s the string to escape
|
||||||
|
@param[in] ensure_ascii whether to escape non-ASCII characters with
|
||||||
|
\uXXXX sequences
|
||||||
|
@return the number of characters required to escape string @a s
|
||||||
|
|
||||||
|
@complexity Linear in the length of string @a s.
|
||||||
|
*/
|
||||||
|
static std::size_t extra_space(const string_t& s,
|
||||||
|
const bool ensure_ascii) noexcept
|
||||||
|
{
|
||||||
|
std::size_t res = 0;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < s.size(); ++i)
|
||||||
|
{
|
||||||
|
switch (s[i])
|
||||||
|
{
|
||||||
|
// control characters that can be escaped with a backslash
|
||||||
|
case '"':
|
||||||
|
case '\\':
|
||||||
|
case '\b':
|
||||||
|
case '\f':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
case '\t':
|
||||||
|
{
|
||||||
|
// from c (1 byte) to \x (2 bytes)
|
||||||
|
res += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// control characters that need \uxxxx escaping
|
||||||
|
case 0x00:
|
||||||
|
case 0x01:
|
||||||
|
case 0x02:
|
||||||
|
case 0x03:
|
||||||
|
case 0x04:
|
||||||
|
case 0x05:
|
||||||
|
case 0x06:
|
||||||
|
case 0x07:
|
||||||
|
case 0x0B:
|
||||||
|
case 0x0E:
|
||||||
|
case 0x0F:
|
||||||
|
case 0x10:
|
||||||
|
case 0x11:
|
||||||
|
case 0x12:
|
||||||
|
case 0x13:
|
||||||
|
case 0x14:
|
||||||
|
case 0x15:
|
||||||
|
case 0x16:
|
||||||
|
case 0x17:
|
||||||
|
case 0x18:
|
||||||
|
case 0x19:
|
||||||
|
case 0x1A:
|
||||||
|
case 0x1B:
|
||||||
|
case 0x1C:
|
||||||
|
case 0x1D:
|
||||||
|
case 0x1E:
|
||||||
|
case 0x1F:
|
||||||
|
{
|
||||||
|
// from c (1 byte) to \uxxxx (6 bytes)
|
||||||
|
res += 5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
if (ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F))
|
||||||
|
{
|
||||||
|
const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
|
||||||
|
// invalid characters will be detected by throw_if_invalid_utf8
|
||||||
|
assert (bytes != std::string::npos);
|
||||||
|
|
||||||
|
if (bytes == 3)
|
||||||
|
{
|
||||||
|
// codepoints that need 4 bytes (i.e., 3 additional
|
||||||
|
// bytes) in UTF-8 need a surrogate pair when \u
|
||||||
|
// escaping is used: from 4 bytes to \uxxxx\uxxxx
|
||||||
|
// (12 bytes)
|
||||||
|
res += (12 - bytes - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// from x bytes to \uxxxx (6 bytes)
|
||||||
|
res += (6 - bytes - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip the additional bytes
|
||||||
|
i += bytes;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void escape_codepoint(int codepoint, string_t& result, std::size_t& pos)
|
||||||
|
{
|
||||||
|
// expecting a proper codepoint
|
||||||
|
assert(0x00 <= codepoint and codepoint <= 0x10FFFF);
|
||||||
|
|
||||||
|
// the last written character was the backslash before the 'u'
|
||||||
|
assert(result[pos] == '\\');
|
||||||
|
|
||||||
|
// write the 'u'
|
||||||
|
result[++pos] = 'u';
|
||||||
|
|
||||||
|
// convert a number 0..15 to its hex representation (0..f)
|
||||||
|
static const std::array<char, 16> hexify =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (codepoint < 0x10000)
|
||||||
|
{
|
||||||
|
// codepoints U+0000..U+FFFF can be represented as \uxxxx.
|
||||||
|
result[++pos] = hexify[(codepoint >> 12) & 0x0F];
|
||||||
|
result[++pos] = hexify[(codepoint >> 8) & 0x0F];
|
||||||
|
result[++pos] = hexify[(codepoint >> 4) & 0x0F];
|
||||||
|
result[++pos] = hexify[codepoint & 0x0F];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// codepoints U+10000..U+10FFFF need a surrogate pair to be
|
||||||
|
// represented as \uxxxx\uxxxx.
|
||||||
|
// http://www.unicode.org/faq/utf_bom.html#utf16-4
|
||||||
|
codepoint -= 0x10000;
|
||||||
|
const int high_surrogate = 0xD800 | ((codepoint >> 10) & 0x3FF);
|
||||||
|
const int low_surrogate = 0xDC00 | (codepoint & 0x3FF);
|
||||||
|
result[++pos] = hexify[(high_surrogate >> 12) & 0x0F];
|
||||||
|
result[++pos] = hexify[(high_surrogate >> 8) & 0x0F];
|
||||||
|
result[++pos] = hexify[(high_surrogate >> 4) & 0x0F];
|
||||||
|
result[++pos] = hexify[high_surrogate & 0x0F];
|
||||||
|
++pos; // backslash is already in output
|
||||||
|
result[++pos] = 'u';
|
||||||
|
result[++pos] = hexify[(low_surrogate >> 12) & 0x0F];
|
||||||
|
result[++pos] = hexify[(low_surrogate >> 8) & 0x0F];
|
||||||
|
result[++pos] = hexify[(low_surrogate >> 4) & 0x0F];
|
||||||
|
result[++pos] = hexify[low_surrogate & 0x0F];
|
||||||
|
}
|
||||||
|
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief dump escaped string
|
||||||
|
|
||||||
|
Escape a string by replacing certain special characters by a sequence of an
|
||||||
|
escape character (backslash) and another character and other control
|
||||||
|
characters by a sequence of "\u" followed by a four-digit hex
|
||||||
|
representation. The escaped string is written to output stream @a o.
|
||||||
|
|
||||||
|
@param[in] s the string to escape
|
||||||
|
@param[in] ensure_ascii whether to escape non-ASCII characters with
|
||||||
|
\uXXXX sequences
|
||||||
|
|
||||||
|
@complexity Linear in the length of string @a s.
|
||||||
|
*/
|
||||||
|
void dump_escaped(const string_t& s, const bool ensure_ascii) const
|
||||||
|
{
|
||||||
|
throw_if_invalid_utf8(s);
|
||||||
|
|
||||||
|
const auto space = extra_space(s, ensure_ascii);
|
||||||
|
if (space == 0)
|
||||||
|
{
|
||||||
|
o->write_characters(s.c_str(), s.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a result string of necessary size
|
||||||
|
string_t result(s.size() + space, '\\');
|
||||||
|
std::size_t pos = 0;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < s.size(); ++i)
|
||||||
|
{
|
||||||
|
switch (s[i])
|
||||||
|
{
|
||||||
|
case '"': // quotation mark (0x22)
|
||||||
|
{
|
||||||
|
result[pos + 1] = '"';
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\\': // reverse solidus (0x5C)
|
||||||
|
{
|
||||||
|
// nothing to change
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\b': // backspace (0x08)
|
||||||
|
{
|
||||||
|
result[pos + 1] = 'b';
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\f': // formfeed (0x0C)
|
||||||
|
{
|
||||||
|
result[pos + 1] = 'f';
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\n': // newline (0x0A)
|
||||||
|
{
|
||||||
|
result[pos + 1] = 'n';
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\r': // carriage return (0x0D)
|
||||||
|
{
|
||||||
|
result[pos + 1] = 'r';
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\t': // horizontal tab (0x09)
|
||||||
|
{
|
||||||
|
result[pos + 1] = 't';
|
||||||
|
pos += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// escape control characters (0x00..0x1F) or, if
|
||||||
|
// ensure_ascii parameter is used, non-ASCII characters
|
||||||
|
if ((0x00 <= s[i] and s[i] <= 0x1F) or
|
||||||
|
(ensure_ascii and (s[i] & 0x80 or s[i] == 0x7F)))
|
||||||
|
{
|
||||||
|
const auto bytes = bytes_following(static_cast<uint8_t>(s[i]));
|
||||||
|
// invalid characters will be detected by throw_if_invalid_utf8
|
||||||
|
assert (bytes != std::string::npos);
|
||||||
|
|
||||||
|
// check that the additional bytes are present
|
||||||
|
assert(i + bytes < s.size());
|
||||||
|
|
||||||
|
// to use \uxxxx escaping, we first need to calculate
|
||||||
|
// the codepoint from the UTF-8 bytes
|
||||||
|
int codepoint = 0;
|
||||||
|
|
||||||
|
// bytes is unsigned type:
|
||||||
|
assert(bytes <= 3);
|
||||||
|
switch (bytes)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
codepoint = s[i] & 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
codepoint = ((s[i] & 0x3F) << 6)
|
||||||
|
+ (s[i + 1] & 0x7F);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
codepoint = ((s[i] & 0x1F) << 12)
|
||||||
|
+ ((s[i + 1] & 0x7F) << 6)
|
||||||
|
+ (s[i + 2] & 0x7F);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
codepoint = ((s[i] & 0xF) << 18)
|
||||||
|
+ ((s[i + 1] & 0x7F) << 12)
|
||||||
|
+ ((s[i + 2] & 0x7F) << 6)
|
||||||
|
+ (s[i + 3] & 0x7F);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
escape_codepoint(codepoint, result, pos);
|
||||||
|
i += bytes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// all other characters are added as-is
|
||||||
|
result[pos++] = s[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(pos == result.size());
|
||||||
|
o->write_characters(result.c_str(), result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief dump an integer
|
||||||
|
|
||||||
|
Dump a given integer to output stream @a o. Works internally with
|
||||||
|
@a number_buffer.
|
||||||
|
|
||||||
|
@param[in] x integer number (signed or unsigned) to dump
|
||||||
|
@tparam NumberType either @a number_integer_t or @a number_unsigned_t
|
||||||
|
*/
|
||||||
|
template<typename NumberType, detail::enable_if_t<
|
||||||
|
std::is_same<NumberType, number_unsigned_t>::value or
|
||||||
|
std::is_same<NumberType, number_integer_t>::value,
|
||||||
|
int> = 0>
|
||||||
|
void dump_integer(NumberType x)
|
||||||
|
{
|
||||||
|
// special case for "0"
|
||||||
|
if (x == 0)
|
||||||
|
{
|
||||||
|
o->write_character('0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool is_negative = (x <= 0) and (x != 0); // see issue #755
|
||||||
|
std::size_t i = 0;
|
||||||
|
|
||||||
|
while (x != 0)
|
||||||
|
{
|
||||||
|
// spare 1 byte for '\0'
|
||||||
|
assert(i < number_buffer.size() - 1);
|
||||||
|
|
||||||
|
const auto digit = std::labs(static_cast<long>(x % 10));
|
||||||
|
number_buffer[i++] = static_cast<char>('0' + digit);
|
||||||
|
x /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_negative)
|
||||||
|
{
|
||||||
|
// make sure there is capacity for the '-'
|
||||||
|
assert(i < number_buffer.size() - 2);
|
||||||
|
number_buffer[i++] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::reverse(number_buffer.begin(), number_buffer.begin() + i);
|
||||||
|
o->write_characters(number_buffer.data(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief dump a floating-point number
|
||||||
|
|
||||||
|
Dump a given floating-point number to output stream @a o. Works internally
|
||||||
|
with @a number_buffer.
|
||||||
|
|
||||||
|
@param[in] x floating-point number to dump
|
||||||
|
*/
|
||||||
|
void dump_float(number_float_t x)
|
||||||
|
{
|
||||||
|
// NaN / inf
|
||||||
|
if (not std::isfinite(x) or std::isnan(x))
|
||||||
|
{
|
||||||
|
o->write_characters("null", 4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get number of digits for a text -> float -> text round-trip
|
||||||
|
static constexpr auto d = std::numeric_limits<number_float_t>::digits10;
|
||||||
|
|
||||||
|
// the actual conversion
|
||||||
|
std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
|
||||||
|
|
||||||
|
// negative value indicates an error
|
||||||
|
assert(len > 0);
|
||||||
|
// check if buffer was large enough
|
||||||
|
assert(static_cast<std::size_t>(len) < number_buffer.size());
|
||||||
|
|
||||||
|
// erase thousands separator
|
||||||
|
if (thousands_sep != '\0')
|
||||||
|
{
|
||||||
|
const auto end = std::remove(number_buffer.begin(),
|
||||||
|
number_buffer.begin() + len, thousands_sep);
|
||||||
|
std::fill(end, number_buffer.end(), '\0');
|
||||||
|
assert((end - number_buffer.begin()) <= len);
|
||||||
|
len = (end - number_buffer.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert decimal point to '.'
|
||||||
|
if (decimal_point != '\0' and decimal_point != '.')
|
||||||
|
{
|
||||||
|
const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
|
||||||
|
if (dec_pos != number_buffer.end())
|
||||||
|
{
|
||||||
|
*dec_pos = '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
|
||||||
|
|
||||||
|
// determine if need to append ".0"
|
||||||
|
const bool value_is_int_like =
|
||||||
|
std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
|
||||||
|
[](char c)
|
||||||
|
{
|
||||||
|
return (c == '.' or c == 'e');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (value_is_int_like)
|
||||||
|
{
|
||||||
|
o->write_characters(".0", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief check whether a string is UTF-8 encoded
|
||||||
|
|
||||||
|
The function checks each byte of a string whether it is UTF-8 encoded. The
|
||||||
|
result of the check is stored in the @a state parameter. The function must
|
||||||
|
be called initially with state 0 (accept). State 1 means the string must
|
||||||
|
be rejected, because the current byte is not allowed. If the string is
|
||||||
|
completely processed, but the state is non-zero, the string ended
|
||||||
|
prematurely; that is, the last byte indicated more bytes should have
|
||||||
|
followed.
|
||||||
|
|
||||||
|
@param[in,out] state the state of the decoding
|
||||||
|
@param[in] byte next byte to decode
|
||||||
|
|
||||||
|
@note The function has been edited: a std::array is used and the code
|
||||||
|
point is not calculated.
|
||||||
|
|
||||||
|
@copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||||
|
@sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||||
|
*/
|
||||||
|
static void decode(uint8_t& state, const uint8_t byte)
|
||||||
|
{
|
||||||
|
static const std::array<uint8_t, 400> utf8d =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
|
||||||
|
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
|
||||||
|
0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
|
||||||
|
0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
|
||||||
|
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
|
||||||
|
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
|
||||||
|
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
|
||||||
|
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t type = utf8d[byte];
|
||||||
|
state = utf8d[256u + state * 16u + type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief throw an exception if a string is not UTF-8 encoded
|
||||||
|
|
||||||
|
@param[in] str UTF-8 string to check
|
||||||
|
@throw type_error.316 if passed string is not UTF-8 encoded
|
||||||
|
|
||||||
|
@since version 3.0.0
|
||||||
|
*/
|
||||||
|
static void throw_if_invalid_utf8(const std::string& str)
|
||||||
|
{
|
||||||
|
// start with state 0 (= accept)
|
||||||
|
uint8_t state = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < str.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto byte = static_cast<uint8_t>(str[i]);
|
||||||
|
decode(state, byte);
|
||||||
|
if (state == 1)
|
||||||
|
{
|
||||||
|
// state 1 means reject
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(byte);
|
||||||
|
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != 0)
|
||||||
|
{
|
||||||
|
// we finish reading, but do not accept: string was incomplete
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::setw(2) << std::uppercase << std::setfill('0') << std::hex << static_cast<int>(static_cast<uint8_t>(str.back()));
|
||||||
|
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// the output of the serializer
|
||||||
|
output_adapter_t<char> o = nullptr;
|
||||||
|
|
||||||
|
/// a (hopefully) large enough character buffer
|
||||||
|
std::array<char, 64> number_buffer{{}};
|
||||||
|
|
||||||
|
/// the locale
|
||||||
|
const std::lconv* loc = nullptr;
|
||||||
|
/// the locale's thousand separator character
|
||||||
|
const char thousands_sep = '\0';
|
||||||
|
/// the locale's decimal point character
|
||||||
|
const char decimal_point = '\0';
|
||||||
|
|
||||||
|
/// the indentation character
|
||||||
|
const char indent_char;
|
||||||
|
|
||||||
|
/// the indentation string
|
||||||
|
string_t indent_string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
76
develop/detail/value_t.hpp
Normal file
76
develop/detail/value_t.hpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array> // array
|
||||||
|
#include <ciso646> // and
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <cstdint> // uint8_t
|
||||||
|
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
///////////////////////////
|
||||||
|
// JSON type enumeration //
|
||||||
|
///////////////////////////
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief the JSON type enumeration
|
||||||
|
|
||||||
|
This enumeration collects the different JSON types. It is internally used to
|
||||||
|
distinguish the stored values, and the functions @ref basic_json::is_null(),
|
||||||
|
@ref basic_json::is_object(), @ref basic_json::is_array(),
|
||||||
|
@ref basic_json::is_string(), @ref basic_json::is_boolean(),
|
||||||
|
@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
|
||||||
|
@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
|
||||||
|
@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
|
||||||
|
@ref basic_json::is_structured() rely on it.
|
||||||
|
|
||||||
|
@note There are three enumeration entries (number_integer, number_unsigned, and
|
||||||
|
number_float), because the library distinguishes these three types for numbers:
|
||||||
|
@ref basic_json::number_unsigned_t is used for unsigned integers,
|
||||||
|
@ref basic_json::number_integer_t is used for signed integers, and
|
||||||
|
@ref basic_json::number_float_t is used for floating-point numbers or to
|
||||||
|
approximate integers which do not fit in the limits of their respective type.
|
||||||
|
|
||||||
|
@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
|
||||||
|
value with the default value for a given type
|
||||||
|
|
||||||
|
@since version 1.0.0
|
||||||
|
*/
|
||||||
|
enum class value_t : std::uint8_t
|
||||||
|
{
|
||||||
|
null, ///< null value
|
||||||
|
object, ///< object (unordered set of name/value pairs)
|
||||||
|
array, ///< array (ordered collection of values)
|
||||||
|
string, ///< string value
|
||||||
|
boolean, ///< boolean value
|
||||||
|
number_integer, ///< number value (signed integer)
|
||||||
|
number_unsigned, ///< number value (unsigned integer)
|
||||||
|
number_float, ///< number value (floating-point)
|
||||||
|
discarded ///< discarded by the the parser callback function
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief comparison operator for JSON types
|
||||||
|
|
||||||
|
Returns an ordering that is similar to Python:
|
||||||
|
- order: null < boolean < number < object < array < string
|
||||||
|
- furthermore, each type is not smaller than itself
|
||||||
|
- discarded values are not comparable
|
||||||
|
|
||||||
|
@since version 1.0.0
|
||||||
|
*/
|
||||||
|
inline bool operator<(const value_t lhs, const value_t rhs) noexcept
|
||||||
|
{
|
||||||
|
static constexpr std::array<std::uint8_t, 8> order = {{
|
||||||
|
0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
|
||||||
|
1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto l_index = static_cast<std::size_t>(lhs);
|
||||||
|
const auto r_index = static_cast<std::size_t>(rhs);
|
||||||
|
return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8024
develop/json.hpp
Normal file
8024
develop/json.hpp
Normal file
File diff suppressed because it is too large
Load diff
63
develop/json_fwd.hpp
Normal file
63
develop/json_fwd.hpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef NLOHMANN_JSON_FWD_HPP
|
||||||
|
#define NLOHMANN_JSON_FWD_HPP
|
||||||
|
|
||||||
|
#include <cstdint> // int64_t, uint64_t
|
||||||
|
#include <map> // map
|
||||||
|
#include <memory> // allocator
|
||||||
|
#include <string> // string
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief namespace for Niels Lohmann
|
||||||
|
@see https://github.com/nlohmann
|
||||||
|
@since version 1.0.0
|
||||||
|
*/
|
||||||
|
namespace nlohmann
|
||||||
|
{
|
||||||
|
/*!
|
||||||
|
@brief default JSONSerializer template argument
|
||||||
|
|
||||||
|
This serializer ignores the template arguments and uses ADL
|
||||||
|
([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl))
|
||||||
|
for serialization.
|
||||||
|
*/
|
||||||
|
template<typename = void, typename = void>
|
||||||
|
struct adl_serializer;
|
||||||
|
|
||||||
|
template<template<typename U, typename V, typename... Args> class ObjectType =
|
||||||
|
std::map,
|
||||||
|
template<typename U, typename... Args> class ArrayType = std::vector,
|
||||||
|
class StringType = std::string, class BooleanType = bool,
|
||||||
|
class NumberIntegerType = std::int64_t,
|
||||||
|
class NumberUnsignedType = std::uint64_t,
|
||||||
|
class NumberFloatType = double,
|
||||||
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
|
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||||
|
adl_serializer>
|
||||||
|
class basic_json;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief JSON Pointer
|
||||||
|
|
||||||
|
A JSON pointer defines a string syntax for identifying a specific value
|
||||||
|
within a JSON document. It can be used with functions `at` and
|
||||||
|
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
|
||||||
|
|
||||||
|
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
|
||||||
|
|
||||||
|
@since version 2.0.0
|
||||||
|
*/
|
||||||
|
class json_pointer;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief default JSON class
|
||||||
|
|
||||||
|
This type is the default specialization of the @ref basic_json class which
|
||||||
|
uses the standard template types.
|
||||||
|
|
||||||
|
@since version 1.0.0
|
||||||
|
*/
|
||||||
|
using json = basic_json<>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
19
doc/Doxyfile
19
doc/Doxyfile
|
@ -1,17 +1,18 @@
|
||||||
# Doxyfile 1.8.14
|
# Doxyfile 1.8.15
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Project related configuration options
|
# Project related configuration options
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
DOXYFILE_ENCODING = UTF-8
|
DOXYFILE_ENCODING = UTF-8
|
||||||
PROJECT_NAME = "JSON for Modern C++"
|
PROJECT_NAME = "JSON for Modern C++"
|
||||||
PROJECT_NUMBER = 2.1.1
|
PROJECT_NUMBER = 3.0.1
|
||||||
PROJECT_BRIEF =
|
PROJECT_BRIEF =
|
||||||
PROJECT_LOGO =
|
PROJECT_LOGO =
|
||||||
OUTPUT_DIRECTORY = .
|
OUTPUT_DIRECTORY = .
|
||||||
CREATE_SUBDIRS = NO
|
CREATE_SUBDIRS = NO
|
||||||
ALLOW_UNICODE_NAMES = NO
|
ALLOW_UNICODE_NAMES = NO
|
||||||
OUTPUT_LANGUAGE = English
|
OUTPUT_LANGUAGE = English
|
||||||
|
OUTPUT_TEXT_DIRECTION = None
|
||||||
BRIEF_MEMBER_DESC = YES
|
BRIEF_MEMBER_DESC = YES
|
||||||
REPEAT_BRIEF = NO
|
REPEAT_BRIEF = NO
|
||||||
ABBREVIATE_BRIEF =
|
ABBREVIATE_BRIEF =
|
||||||
|
@ -27,11 +28,11 @@ MULTILINE_CPP_IS_BRIEF = NO
|
||||||
INHERIT_DOCS = YES
|
INHERIT_DOCS = YES
|
||||||
SEPARATE_MEMBER_PAGES = YES
|
SEPARATE_MEMBER_PAGES = YES
|
||||||
TAB_SIZE = 4
|
TAB_SIZE = 4
|
||||||
ALIASES = "complexity=@par Complexity\n" \
|
ALIASES = "complexity=@par Complexity^^" \
|
||||||
"liveexample{2}=@par Example\n \1 \n @includelineno \2.cpp \n Output (play with this example @htmlinclude \2.link):\n @verbinclude \2.output \n The <a href= https://github.com/nlohmann/json/blob/develop/doc/examples/\2.cpp>example code</a> above can be translated with @verbatim g++ -std=c++11 -Isrc doc/examples/\2.cpp -o \2 @endverbatim" \
|
"liveexample{2}=@par Example^^ \1 ^^ @includelineno \2.cpp \n Output (play with this example @htmlinclude \2.link):^^ @verbinclude \2.output ^^ The <a href= https://github.com/nlohmann/json/blob/develop/doc/examples/\2.cpp>example code</a> above can be translated with @verbatim g++ -std=c++11 -Isrc doc/examples/\2.cpp -o \2 @endverbatim" \
|
||||||
"requirement=@par Requirements\n" \
|
"requirement=@par Requirements^^" \
|
||||||
"exceptionsafety=@par Exception safety\n" \
|
"exceptionsafety=@par Exception safety^^" \
|
||||||
"iterators=@par Iterator validity\n"
|
"iterators=@par Iterator validity^^"
|
||||||
TCL_SUBST =
|
TCL_SUBST =
|
||||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||||
OPTIMIZE_OUTPUT_JAVA = NO
|
OPTIMIZE_OUTPUT_JAVA = NO
|
||||||
|
@ -107,7 +108,8 @@ WARN_LOGFILE =
|
||||||
# Configuration options related to the input files
|
# Configuration options related to the input files
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
INPUT = ../src/json.hpp \
|
INPUT = ../src/json.hpp \
|
||||||
index.md faq.md
|
index.md \
|
||||||
|
faq.md
|
||||||
INPUT_ENCODING = UTF-8
|
INPUT_ENCODING = UTF-8
|
||||||
FILE_PATTERNS =
|
FILE_PATTERNS =
|
||||||
RECURSIVE = NO
|
RECURSIVE = NO
|
||||||
|
@ -138,6 +140,7 @@ USE_HTAGS = NO
|
||||||
VERBATIM_HEADERS = NO
|
VERBATIM_HEADERS = NO
|
||||||
CLANG_ASSISTED_PARSING = YES
|
CLANG_ASSISTED_PARSING = YES
|
||||||
CLANG_OPTIONS = -std=c++11
|
CLANG_OPTIONS = -std=c++11
|
||||||
|
CLANG_COMPILATION_DATABASE_PATH = 0
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to the alphabetical class index
|
# Configuration options related to the alphabetical class index
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
|
@ -63,7 +63,7 @@ doxygen: create_output create_links
|
||||||
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
|
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
|
||||||
|
|
||||||
upload: clean doxygen check_output
|
upload: clean doxygen check_output
|
||||||
cd html ; ../scripts/git-update-ghpages nlohmann/json
|
scripts/git-update-ghpages nlohmann/json html
|
||||||
rm -fr html
|
rm -fr html
|
||||||
open http://nlohmann.github.io/json/
|
open http://nlohmann.github.io/json/
|
||||||
|
|
||||||
|
|
BIN
doc/avatars.png
Normal file
BIN
doc/avatars.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 557 KiB |
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/yCTCvQi0ZVxGuL3L"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/Op57X0V7fTf2tdwl"><b>online</b></a>
|
|
@ -79,6 +79,17 @@ int main()
|
||||||
std::cout << e.what() << '\n';
|
std::cout << e.what() << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// out_of_range.403
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// try to use a JSON pointer to an nonexistent object key
|
||||||
|
json::const_reference ref = j.at("/foo"_json_pointer);
|
||||||
|
}
|
||||||
|
catch (json::out_of_range& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
// out_of_range.404
|
// out_of_range.404
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/mlj2WqOtwBidvuJx"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/3RO0naQeY3cWXNAz"><b>online</b></a>
|
|
@ -8,4 +8,5 @@
|
||||||
[json.exception.parse_error.109] parse error: array index 'one' is not a number
|
[json.exception.parse_error.109] parse error: array index 'one' is not a number
|
||||||
[json.exception.out_of_range.401] array index 4 is out of range
|
[json.exception.out_of_range.401] array index 4 is out of range
|
||||||
[json.exception.out_of_range.402] array index '-' (2) is out of range
|
[json.exception.out_of_range.402] array index '-' (2) is out of range
|
||||||
|
[json.exception.out_of_range.403] key 'foo' not found
|
||||||
[json.exception.out_of_range.404] unresolved reference token 'foo'
|
[json.exception.out_of_range.404] unresolved reference token 'foo'
|
||||||
|
|
|
@ -55,6 +55,17 @@ int main()
|
||||||
std::cout << e.what() << '\n';
|
std::cout << e.what() << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// out_of_range.403
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// try to use a JSON pointer to an nonexistent object key
|
||||||
|
json::const_reference ref = j.at("/foo"_json_pointer);
|
||||||
|
}
|
||||||
|
catch (json::out_of_range& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
// out_of_range.404
|
// out_of_range.404
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/vHKlyYl9DwaUO9UN"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/LwtGNqrGgjcWey3P"><b>online</b></a>
|
|
@ -5,4 +5,5 @@
|
||||||
[json.exception.parse_error.109] parse error: array index 'one' is not a number
|
[json.exception.parse_error.109] parse error: array index 'one' is not a number
|
||||||
[json.exception.out_of_range.401] array index 4 is out of range
|
[json.exception.out_of_range.401] array index 4 is out of range
|
||||||
[json.exception.out_of_range.402] array index '-' (2) is out of range
|
[json.exception.out_of_range.402] array index '-' (2) is out of range
|
||||||
|
[json.exception.out_of_range.403] key 'foo' not found
|
||||||
[json.exception.out_of_range.404] unresolved reference token 'foo'
|
[json.exception.out_of_range.404] unresolved reference token 'foo'
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/DKPu3VnNVgsQbaSX"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/QJlGaO7RqgvibCbR"><b>online</b></a>
|
|
@ -28,4 +28,15 @@ int main()
|
||||||
std::cout << "strings:" << '\n'
|
std::cout << "strings:" << '\n'
|
||||||
<< j_string.dump() << '\n'
|
<< j_string.dump() << '\n'
|
||||||
<< j_string.dump(-1, ' ', true) << '\n';
|
<< j_string.dump(-1, ' ', true) << '\n';
|
||||||
|
|
||||||
|
// create JSON value with invalid UTF-8 byte sequence
|
||||||
|
json j_invalid = "\xF0\xA4\xAD\xC0";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::cout << j_invalid.dump() << std::endl;
|
||||||
|
}
|
||||||
|
catch (json::type_error& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/UnV6etCOZZRZpYyB"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/mHH9TibITCYPpLwy"><b>online</b></a>
|
|
@ -50,3 +50,4 @@ arrays:
|
||||||
strings:
|
strings:
|
||||||
"Hellö 😀!"
|
"Hellö 😀!"
|
||||||
"Hell\u00f6 \ud83d\ude00!"
|
"Hell\u00f6 \ud83d\ude00!"
|
||||||
|
[json.exception.type_error.316] invalid UTF-8 byte at index 3: 0xC0
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/TpEB8tou7Wuyprkm"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/EBqdb2uhXyKTbr9f"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/X9wSCDZNg82T3oix"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/NkSIj6G89DXRXX9u"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/9MjZO7niLtZaJIIx"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/DlKRo80ESI428gH8"><b>online</b></a>
|
23
doc/examples/items.cpp
Normal file
23
doc/examples/items.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// create JSON values
|
||||||
|
json j_object = {{"one", 1}, {"two", 2}};
|
||||||
|
json j_array = {1, 2, 4, 8, 16};
|
||||||
|
|
||||||
|
// example for an object
|
||||||
|
for (auto& x : j_object.items())
|
||||||
|
{
|
||||||
|
std::cout << "key: " << x.key() << ", value: " << x.value() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// example for an array
|
||||||
|
for (auto& x : j_array.items())
|
||||||
|
{
|
||||||
|
std::cout << "key: " << x.key() << ", value: " << x.value() << '\n';
|
||||||
|
}
|
||||||
|
}
|
1
doc/examples/items.link
Normal file
1
doc/examples/items.link
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<a target="_blank" href="https://wandbox.org/permlink/E7h0HOuw778Ski8S"><b>online</b></a>
|
7
doc/examples/items.output
Normal file
7
doc/examples/items.output
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
key: one, value: 1
|
||||||
|
key: two, value: 2
|
||||||
|
key: 0, value: 1
|
||||||
|
key: 1, value: 2
|
||||||
|
key: 2, value: 4
|
||||||
|
key: 3, value: 8
|
||||||
|
key: 4, value: 16
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/M9jbzex4Xj2l25ce"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/mtnyQGVjmAoHVvaP"><b>online</b></a>
|
|
@ -2,16 +2,16 @@
|
||||||
"compiler": {
|
"compiler": {
|
||||||
"c++": "201103",
|
"c++": "201103",
|
||||||
"family": "clang",
|
"family": "clang",
|
||||||
"version": "8.1.0 (clang-802.0.42)"
|
"version": "9.0.0 (clang-900.0.39.2)"
|
||||||
},
|
},
|
||||||
"copyright": "(C) 2013-2017 Niels Lohmann",
|
"copyright": "(C) 2013-2017 Niels Lohmann",
|
||||||
"name": "JSON for Modern C++",
|
"name": "JSON for Modern C++",
|
||||||
"platform": "apple",
|
"platform": "apple",
|
||||||
"url": "https://github.com/nlohmann/json",
|
"url": "https://github.com/nlohmann/json",
|
||||||
"version": {
|
"version": {
|
||||||
"major": 2,
|
"major": 3,
|
||||||
"minor": 1,
|
"minor": 0,
|
||||||
"patch": 1,
|
"patch": 1,
|
||||||
"string": "2.1.1"
|
"string": "3.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/atomVJKVgMTIh1iT"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/qrMPH8rv4FBqE2Ri"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/0Ec7N50CDWAWPWEE"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/9j30rS3lws0iiHPd"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/iG5QR4oqgCLaEedf"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/bYLh2weuTo9jkR5V"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/fs7n9Xh41pSIxK4g"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/12CMBHX694yrt0i9"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/2cJ6aEoQItmXOyEe"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/YDBQGKfyJUePDpRe"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/IvIGk03HXpuyv271"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/DhwTP8AtK5UoeF55"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/a8XRIpnVOQp3zYKV"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/c4K2s7QuFNfXd3j7"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/JZ8P4ruGJTLfOFHJ"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/GKABhikWJQTHeX7z"><b>online</b></a>
|
|
@ -1,6 +1,5 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/U9n8nunch9ti8XlQ"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/QVr68rXTR2kztpbl"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/m6XKVQBrhB6jXGOe"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/ze9XofH6xAKtIha1"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/b66iY9hOdCHEG8FV"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/k8bMpRumLfsA8ylM"><b>online</b></a>
|
|
@ -1,5 +1,4 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip> // for std::setw
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
<a target="_blank" href="https://wandbox.org/permlink/WKkNkEuGbsxPknyT"><b>online</b></a>
|
<a target="_blank" href="https://wandbox.org/permlink/hvj393WgvjIaiUZe"><b>online</b></a>
|
63
doc/index.md
63
doc/index.md
|
@ -28,6 +28,12 @@ These pages contain the API documentation of JSON for Modern C++, a C++11 header
|
||||||
- @link nlohmann::basic_json::get_ref get_ref @endlink -- get a value reference
|
- @link nlohmann::basic_json::get_ref get_ref @endlink -- get a value reference
|
||||||
- @link nlohmann::basic_json::operator ValueType() const operator ValueType @endlink -- get a value (implicit conversion)
|
- @link nlohmann::basic_json::operator ValueType() const operator ValueType @endlink -- get a value (implicit conversion)
|
||||||
- @link nlohmann::basic_json::value value @endlink -- get a value from an object and return default value if key is not present
|
- @link nlohmann::basic_json::value value @endlink -- get a value from an object and return default value if key is not present
|
||||||
|
- exceptions
|
||||||
|
- @link nlohmann::basic_json::parse_error parse_error @endlink for exceptions indicating a parse error
|
||||||
|
- @link nlohmann::basic_json::invalid_iterator invalid_iterator @endlink for exceptions indicating errors with iterators
|
||||||
|
- @link nlohmann::basic_json::type_error type_error @endlink for exceptions indicating executing a member function with a wrong type
|
||||||
|
- @link nlohmann::basic_json::out_of_range out_of_range @endlink for exceptions indicating access out of the defined range
|
||||||
|
- @link nlohmann::basic_json::other_error other_error @endlink for exceptions indicating other library errors
|
||||||
- lexicographical comparison operators
|
- lexicographical comparison operators
|
||||||
- serialization
|
- serialization
|
||||||
- deserialization
|
- deserialization
|
||||||
|
@ -45,6 +51,8 @@ These pages contain the API documentation of JSON for Modern C++, a C++11 header
|
||||||
|
|
||||||
The container functions known from STL have been extended to support the different value types from JSON. However, not all functions can be applied to all value types. Note that the signature of some functions differ between the types; for instance, `at` may be called with either a string to address a key in an object or with an integer to address a value in an array.
|
The container functions known from STL have been extended to support the different value types from JSON. However, not all functions can be applied to all value types. Note that the signature of some functions differ between the types; for instance, `at` may be called with either a string to address a key in an object or with an integer to address a value in an array.
|
||||||
|
|
||||||
|
Note that this table only lists those exceptions thrown due to the type. For instance, the @link nlohmann::basic_json::at(const typename object_t::key_type & key) `at` @endlink function will always throw a @link nlohmann::basic_json::type_error `json::type_error` @endlink exception when called for a string type. When called for an array, it *may* throw an @link nlohmann::basic_json::out_of_range `json::out_of_range` @endlink exception if the passed index is invalid.
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th rowspan="2">group</td>
|
<th rowspan="2">group</td>
|
||||||
|
@ -137,18 +145,18 @@ The container functions known from STL have been extended to support the differe
|
||||||
<td>`at`</td>
|
<td>`at`</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::at(const typename object_t::key_type & key) `at` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::at(const typename object_t::key_type & key) `at` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::at(size_type) `at` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::at(size_type) `at` @endlink</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (304)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (304)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (304)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (304)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`operator[]`</td>
|
<td>`operator[]`</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::operator[](const typename object_t::key_type &key) `operator[]` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::operator[](const typename object_t::key_type &key) `operator[]` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::operator[](size_type) `operator[]` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::operator[](size_type) `operator[]` @endlink</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (305)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (305)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (305)</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::operator[](const typename object_t::key_type & key) `operator[]` @endlink (creates object)<br>@link nlohmann::basic_json::operator[](size_type) `operator[]` @endlink (creates array)</td>
|
<td class="ok_green">@link nlohmann::basic_json::operator[](const typename object_t::key_type & key) `operator[]` @endlink (creates object)<br>@link nlohmann::basic_json::operator[](size_type) `operator[]` @endlink (creates array)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -158,7 +166,7 @@ The container functions known from STL have been extended to support the differe
|
||||||
<td class="ok_green">@link nlohmann::basic_json::front `front` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::front `front` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::front `front` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::front `front` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::front `front` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::front `front` @endlink</td>
|
||||||
<td class="nok_throws">throws `std::out_of_range`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::invalid_iterator `json::invalid_iterator` @endlink (214)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`back`</td>
|
<td>`back`</td>
|
||||||
|
@ -167,7 +175,7 @@ The container functions known from STL have been extended to support the differe
|
||||||
<td class="ok_green">@link nlohmann::basic_json::back `back` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::back `back` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::back `back` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::back `back` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::back `back` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::back `back` @endlink</td>
|
||||||
<td class="nok_throws">throws `std::out_of_range`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::invalid_iterator `json::invalid_iterator` @endlink (214)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan="3">capacity</td>
|
<td rowspan="3">capacity</td>
|
||||||
|
@ -198,7 +206,7 @@ The container functions known from STL have been extended to support the differe
|
||||||
<td class="ok_green">@link nlohmann::basic_json::max_size `max_size` @endlink (returns `0`)</td>
|
<td class="ok_green">@link nlohmann::basic_json::max_size `max_size` @endlink (returns `0`)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan="6">modifiers</td>
|
<td rowspan="7">modifiers</td>
|
||||||
<td>`clear`</td>
|
<td>`clear`</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::clear `clear` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::clear `clear` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::clear `clear` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::clear `clear` @endlink</td>
|
||||||
|
@ -209,12 +217,12 @@ The container functions known from STL have been extended to support the differe
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`insert`</td>
|
<td>`insert`</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (309)</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::insert `insert` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::insert `insert` @endlink</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (309)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (309)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (309)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (309)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`erase`</td>
|
<td>`erase`</td>
|
||||||
|
@ -223,26 +231,35 @@ The container functions known from STL have been extended to support the differe
|
||||||
<td class="ok_green">@link nlohmann::basic_json::erase `erase` @endlink (converts to null)</td>
|
<td class="ok_green">@link nlohmann::basic_json::erase `erase` @endlink (converts to null)</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::erase `erase` @endlink (converts to null)</td>
|
<td class="ok_green">@link nlohmann::basic_json::erase `erase` @endlink (converts to null)</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::erase `erase` @endlink (converts to null)</td>
|
<td class="ok_green">@link nlohmann::basic_json::erase `erase` @endlink (converts to null)</td>
|
||||||
<td class="nok_throws">throws</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (307)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`push_back`</td>
|
<td>`push_back`</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::push_back(const typename object_t::value_type & val) `push_back` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::push_back(const typename object_t::value_type & val) `push_back` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::push_back(const nlohmann::basic_json &) `push_back` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::push_back(const nlohmann::basic_json &) `push_back` @endlink</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (308)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (308)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (308)</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::push_back(const typename object_t::value_type & val) `push_back` @endlink (creates object)<br>@link nlohmann::basic_json::push_back(const nlohmann::basic_json &) `push_back` @endlink (creates array)</td>
|
<td class="ok_green">@link nlohmann::basic_json::push_back(const typename object_t::value_type & val) `push_back` @endlink (creates object)<br>@link nlohmann::basic_json::push_back(const nlohmann::basic_json &) `push_back` @endlink (creates array)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`emplace` / `emplace_back`</td>
|
<td>`emplace` / `emplace_back`</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::emplace() `emplace` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::emplace() `emplace` @endlink</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::emplace_back() `emplace_back` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::emplace_back() `emplace_back` @endlink</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (311)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (311)</td>
|
||||||
<td class="nok_throws">throws `std::domain_error`</td>
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (311)</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::emplace() `emplace` @endlink (creates object)<br>@link nlohmann::basic_json::emplace_back() `emplace_back` @endlink (creates array)</td>
|
<td class="ok_green">@link nlohmann::basic_json::emplace() `emplace` @endlink (creates object)<br>@link nlohmann::basic_json::emplace_back() `emplace_back` @endlink (creates array)</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>`update`</td>
|
||||||
|
<td class="ok_green">@link nlohmann::basic_json::update() `update` @endlink</td>
|
||||||
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (312)</td>
|
||||||
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (312)</td>
|
||||||
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (312)</td>
|
||||||
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (312)</td>
|
||||||
|
<td class="nok_throws">throws @link nlohmann::basic_json::type_error `json::type_error` @endlink (312)</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`swap`</td>
|
<td>`swap`</td>
|
||||||
<td class="ok_green">@link nlohmann::basic_json::swap `swap` @endlink</td>
|
<td class="ok_green">@link nlohmann::basic_json::swap `swap` @endlink</td>
|
||||||
|
@ -278,4 +295,4 @@ The container functions known from STL have been extended to support the differe
|
||||||
@author [Niels Lohmann](http://nlohmann.me)
|
@author [Niels Lohmann](http://nlohmann.me)
|
||||||
@see https://github.com/nlohmann/json to download the source code
|
@see https://github.com/nlohmann/json to download the source code
|
||||||
|
|
||||||
@version 2.1.1
|
@version 3.0.1
|
||||||
|
|
BIN
doc/json.gif
BIN
doc/json.gif
Binary file not shown.
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 4.2 MiB |
|
@ -1,221 +1,193 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env bash
|
||||||
# git update-ghpages user/repo -b gh-pages -p manual/ -i
|
set -o errexit
|
||||||
|
|
||||||
require 'fileutils'
|
copy_contents() {
|
||||||
require 'tmpdir'
|
local source="$1"
|
||||||
|
status "Copying contents from $source"
|
||||||
module Params
|
if [[ ! "$dryrun" == "1" ]]; then
|
||||||
def extract(what) i = index(what) and slice!(i, 2)[1] end;
|
(cd "$source" >/dev/null && tar c .) | tar xv
|
||||||
def first_is(what) shift if what.include?(self.first); end
|
|
||||||
def self.[](*what) what.extend Params; end
|
|
||||||
def ===(argv) argv.first_is(self); end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
|
|
||||||
ARGV.extend Params
|
|
||||||
|
|
||||||
class CLI
|
|
||||||
# CLI options
|
|
||||||
attr_reader :prefix #=> "doc/"
|
|
||||||
attr_reader :input #=> "/home/me/projects/foo"
|
|
||||||
attr_reader :message #=> "Updated"
|
|
||||||
attr_reader :repo #=> "git@github.com:me/project.git"
|
|
||||||
attr_reader :url #=> "http://me.github.com/project"
|
|
||||||
attr_reader :branch #=> "gh-pages"
|
|
||||||
|
|
||||||
def verbose?() @verbose; end
|
|
||||||
def force?() @force; end
|
|
||||||
def simulate?() @simulate; end
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
# Switches
|
|
||||||
@verbose = !! (ARGV.extract('--verbose') || ARGV.delete('-v'))
|
|
||||||
@simulate = !! (ARGV.extract('--simulate') || ARGV.delete('-s'))
|
|
||||||
@force = !! (ARGV.delete('--force') || ARGV.delete('-f'))
|
|
||||||
|
|
||||||
# Stuff
|
|
||||||
@prefix = ARGV.extract('--prefix') || ARGV.extract('-p') || ''
|
|
||||||
@input = File.expand_path(ARGV.extract('--input') || ARGV.extract('-i') || '.')
|
|
||||||
@message = ARGV.extract('--message') || ARGV.extract('-m') || 'Update'
|
|
||||||
|
|
||||||
# Github info
|
|
||||||
branch = ARGV.extract('--branch') || ARGV.extract('-b') || nil
|
|
||||||
@repo, @url, @branch = get_github_info(ARGV.shift, branch)
|
|
||||||
end
|
|
||||||
|
|
||||||
def git_current_branch
|
|
||||||
`git rev-parse --abbrev-ref HEAD`.strip
|
|
||||||
end
|
|
||||||
|
|
||||||
def git_deploy
|
|
||||||
in_temp_path do |temppath|
|
|
||||||
status "Cloning repository"
|
|
||||||
system! "git clone #{repo} -b #{branch} #{temppath}"
|
|
||||||
|
|
||||||
if git_current_branch != branch
|
|
||||||
status "Warning: No #{branch} branch found in repo, creating one."
|
|
||||||
return git_deploy_force
|
|
||||||
end
|
|
||||||
|
|
||||||
copy_files input, File.join(temppath, prefix)
|
|
||||||
|
|
||||||
status "Committing files"
|
|
||||||
system! "git add .; git add -u; git commit -m #{message.to_s.inspect}"
|
|
||||||
|
|
||||||
unless simulate?
|
|
||||||
status "Updating repo"
|
|
||||||
system! "git push origin #{branch}"
|
|
||||||
end
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def git_deploy_force
|
|
||||||
in_temp_path do |temppath|
|
|
||||||
status "Creating new repository"
|
|
||||||
system! "git init ."
|
|
||||||
system! "git checkout -b gh-pages"
|
|
||||||
|
|
||||||
copy_files input, File.join(temppath, prefix)
|
|
||||||
|
|
||||||
status "Committing files"
|
|
||||||
system! "git add . && git commit -m #{message.to_s.inspect}"
|
|
||||||
|
|
||||||
unless simulate?
|
|
||||||
status "Updating repo"
|
|
||||||
system! "git push #{repo} gh-pages:#{branch} --force"
|
|
||||||
end
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_github_info(repo, branch=nil, prefix=nil)
|
|
||||||
if github_format?(repo)
|
|
||||||
user, repo_name = repo.split('/')
|
|
||||||
r = "git@github.com:#{repo}.git"
|
|
||||||
|
|
||||||
# User page or project page?
|
|
||||||
if repo_name =~ /\.github\.com/
|
|
||||||
[r, "http://#{repo_name}/#{prefix}", branch || 'master' ]
|
|
||||||
else
|
else
|
||||||
[r, "http://#{user}.github.com/#{repo_name}/#{prefix}", branch || 'gh-pages' ]
|
_ "(cd \"$source\" >/dev/null && tar c .) | tar xv"
|
||||||
end
|
fi
|
||||||
else
|
}
|
||||||
[repo, nil, branch]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def run!
|
# Sets git config
|
||||||
unless repo
|
set_config() {
|
||||||
print_help
|
if [ -n "$GIT_NAME" ]; then _ git config user.name "$GIT_NAME"; fi
|
||||||
exit 128
|
if [ -n "$GIT_EMAIL" ]; then _ git config user.email "$GIT_EMAIL"; fi
|
||||||
end
|
}
|
||||||
|
|
||||||
status "Deploying to #{repo} (branch #{branch})"
|
# Runs the deployment
|
||||||
msg "NOTE: Running in simulation mode." if simulate?
|
run() {
|
||||||
msg "WARNING: If the repository has gh-pages history, it with be overriden." if force? && !simulate?
|
if [ ! -d "$source" ]; then
|
||||||
|
echo "Source is not a directory: $source"
|
||||||
result = force? ? git_deploy_force : git_deploy
|
|
||||||
|
|
||||||
if result
|
|
||||||
puts ""
|
|
||||||
status "Done."
|
|
||||||
msg "See: #{url}" if url && !simulate?
|
|
||||||
else
|
|
||||||
tip "Failed."
|
|
||||||
exit 1
|
exit 1
|
||||||
end
|
fi
|
||||||
end
|
|
||||||
|
|
||||||
def status(str)
|
local tmpdir="$(mktemp -d)"
|
||||||
puts "#{c('===>',34)} #{c(str, 32)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def msg(str)
|
if [[ "$force" == "1" ]]; then
|
||||||
puts " #{c(str, 32)}"
|
_ cd "$tmpdir"
|
||||||
end
|
_ git init
|
||||||
|
_ git checkout -b "$branch"
|
||||||
|
copy_contents "$source"
|
||||||
|
if [[ "$useenv" == "1" ]]; then set_config; fi
|
||||||
|
_ git add -A .
|
||||||
|
git_commit
|
||||||
|
git_push --force
|
||||||
|
else
|
||||||
|
_ cd "$tmpdir"
|
||||||
|
_ git clone "$repo" . -b "$branch" || ( \
|
||||||
|
_ git init && \
|
||||||
|
_ git checkout -b "$branch")
|
||||||
|
if [[ "$keep" == "0" ]]; then _ rm -rf ./*; fi
|
||||||
|
copy_contents "$source"
|
||||||
|
if [[ "$useenv" == "1" ]]; then set_config; fi
|
||||||
|
_ git add -A .
|
||||||
|
git_commit || true
|
||||||
|
git_push
|
||||||
|
fi
|
||||||
|
_ rm -rf "$tmpdir"
|
||||||
|
status_ "Done"
|
||||||
|
}
|
||||||
|
|
||||||
def c(str, color)
|
git_commit() {
|
||||||
"\033[#{color}m#{str}\033[0m"
|
if [ -z "$author" ]; then
|
||||||
end
|
_ git commit -m "$message"
|
||||||
|
else
|
||||||
|
_ git commit -m "$message" --author "$author"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
def print_help
|
git_push() {
|
||||||
tip \
|
if [ -z "$GITHUB_TOKEN" ]; then
|
||||||
%{Usage: git update-ghpages username/repository [options]
|
_ git push "${repo}" "$branch" "$@"
|
||||||
|
else
|
||||||
|
status "Pushing via \$GITHUB_TOKEN $@"
|
||||||
|
_ git push "https://${GITHUB_TOKEN}@github.com/${repospec}.git" "$branch" "$@" \
|
||||||
|
--quiet >/dev/null 2>&1 || \
|
||||||
|
( status_ "Failed to push"; exit 1 )
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
Flags:
|
status() {
|
||||||
-f, --force Force an update (WARNING: kills the history!)
|
echo -e "\n\033[34m==>\033[0;1m" "$@\033[0m"
|
||||||
-s, --simulate Creates the repository, but doesn't push.
|
}
|
||||||
-v, --verbose Verbose mode
|
status_() {
|
||||||
|
echo -e "\033[33;1m==>\033[0m" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
Options:
|
_() {
|
||||||
-p PATH, --prefix The prefix
|
echo ""
|
||||||
-i PATH, --input Input (defaults to current directory)
|
status_ "$@"
|
||||||
-b BRANCH, --branch The branch to deploy to (defaults to gh-pages)
|
if [[ ! "$dryrun" == "1" ]]; then "$@"; fi
|
||||||
-m MSG, --message Commit message (defaults to 'Update')
|
}
|
||||||
|
|
||||||
Examples:
|
help() {
|
||||||
|
local cmd="$(basename $0)"
|
||||||
|
echo 'Usage:'
|
||||||
|
echo " $cmd <REPO> <SOURCE>"
|
||||||
|
echo ''
|
||||||
|
echo 'Parameters:'
|
||||||
|
echo " REPO repository to push to in 'user/repo' form"
|
||||||
|
echo " SOURCE path to upload to repository's gh-pages branch"
|
||||||
|
echo ''
|
||||||
|
echo 'Options:'
|
||||||
|
echo ' -h, --help show help screen'
|
||||||
|
echo ' -f, --force force push'
|
||||||
|
echo ' -n, --dry-run run in simulation mode'
|
||||||
|
echo ' -e, --use-env pick up arguments from environment variables'
|
||||||
|
echo ' -b, --branch use this branch name (default: gh-pages)'
|
||||||
|
echo ' -a, --author set the author'
|
||||||
|
echo ' -k, --keep keep existing files in the repo'
|
||||||
|
echo ''
|
||||||
|
echo 'Env var options:'
|
||||||
|
echo ' GITHUB_TOKEN if set, use this to push to the repo'
|
||||||
|
echo ''
|
||||||
|
echo 'Optional env vars:'
|
||||||
|
echo " Run with '-e' to enable the use of these variables."
|
||||||
|
echo " GIT_NAME set this as the repos user.name"
|
||||||
|
echo ' GIT_EMAIL set this as the repos user.email'
|
||||||
|
echo ' GITHUB_REPO substitute as the REPO (1st argument)'
|
||||||
|
echo ' GIT_SOURCE substitute as the SOURCE (2nd argument)'
|
||||||
|
echo ' GIT_BRANCH use this branch name (--branch)'
|
||||||
|
echo ''
|
||||||
|
echo 'Example:'
|
||||||
|
echo " $cmd rstacruz/myproject doc"
|
||||||
|
echo " # pushes './doc' into the gh-pages branch of rstacruz/myproject"
|
||||||
|
echo ''
|
||||||
|
echo " export GITHUB_REPO='xyz/abc'"
|
||||||
|
echo " export GIT_SOURCE='docs'"
|
||||||
|
echo " $cmd -e"
|
||||||
|
echo " # pushes './doc' into the gh-pages branch of xyz/abc"
|
||||||
|
}
|
||||||
|
|
||||||
Update the repo 'coffee' of github user 'james' with the files from the
|
#
|
||||||
current directory. The files will be in http://james.github.com/coffee.
|
# Defaults
|
||||||
|
#
|
||||||
|
|
||||||
$ git update-ghpages james/coffee
|
force=0
|
||||||
|
dryrun=0
|
||||||
|
repospec=
|
||||||
|
source=
|
||||||
|
branch=
|
||||||
|
message="Update"
|
||||||
|
useenv=0
|
||||||
|
author=""
|
||||||
|
keep=0
|
||||||
|
|
||||||
Same as above, but take the files from 'doc/'.
|
#
|
||||||
|
# Parse args
|
||||||
|
#
|
||||||
|
|
||||||
$ git update-ghpages james/coffee -i doc
|
while [[ "$1" =~ ^- && ! "$1" == '--' ]]; do case $1 in
|
||||||
|
-h | --help )
|
||||||
|
help
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
-b | --branch )
|
||||||
|
shift
|
||||||
|
branch="$1"
|
||||||
|
;;
|
||||||
|
-n | --dry-run )
|
||||||
|
dryrun=1
|
||||||
|
;;
|
||||||
|
-e | --use-env )
|
||||||
|
useenv=1
|
||||||
|
;;
|
||||||
|
-k | --keep )
|
||||||
|
keep=1
|
||||||
|
;;
|
||||||
|
-a | --author)
|
||||||
|
shift
|
||||||
|
author="$1"
|
||||||
|
;;
|
||||||
|
-f | --force )
|
||||||
|
force=1
|
||||||
|
;;
|
||||||
|
esac; shift; done
|
||||||
|
if [[ "$1" == '--' ]]; then shift; fi
|
||||||
|
|
||||||
Same as the first, but the files will instead be in
|
if [[ "$useenv" == "1" ]] && [[ -n "$GIT_BRANCH" ]] && [[ -z "$branch" ]]; then
|
||||||
http://james.github.com/coffee/manual.
|
branch="$GIT_BRANCH"
|
||||||
|
fi
|
||||||
|
|
||||||
$ git update-ghpages james/coffee -i doc -p manual
|
if [[ "$useenv" == "1" ]] && [[ -n "$GITHUB_REPO" ]] && [[ -n "$GIT_SOURCE" ]] && [[ -z "$2" ]]; then
|
||||||
}.gsub(/^ {4}/, '')
|
repospec="$GITHUB_REPO"
|
||||||
end
|
source="$GIT_SOURCE"
|
||||||
|
else
|
||||||
|
repospec="$1"
|
||||||
|
source="$2"
|
||||||
|
fi
|
||||||
|
|
||||||
private # Helpers
|
: ${branch:="gh-pages"}
|
||||||
|
|
||||||
def tip(msg)
|
if [ -z "$source" ]; then
|
||||||
$stderr.write "#{msg}\n"
|
help
|
||||||
end
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
def github_format?(str)
|
source="`pwd -LP`/$source"
|
||||||
str =~ /^([A-Za-z0-9\-_]+)\/([A-Za-z0-9\-_\.]+)$/
|
repo="https://github.com/${repospec}.git"
|
||||||
end
|
|
||||||
|
|
||||||
# Performs actions inside a temp path.
|
run
|
||||||
def in_temp_path(&blk)
|
|
||||||
require 'tmpdir'
|
|
||||||
Dir.mktmpdir do |dir|
|
|
||||||
Dir.chdir(dir) { yield dir }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def system!(str)
|
|
||||||
puts `#{str} 2>&1`.strip.gsub(/^/, " ")
|
|
||||||
raise "Failed with exit code #{$?.to_i}" unless $?.to_i == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the current branch name
|
|
||||||
def git_branch
|
|
||||||
`git symbolic-ref HEAD`.strip.split('/').last
|
|
||||||
end
|
|
||||||
|
|
||||||
# Copy files from source folder to another
|
|
||||||
def copy_files(from, to)
|
|
||||||
status "Copying files #{from} => #{to}..." if verbose?
|
|
||||||
|
|
||||||
Dir["#{from}/**/*"].each do |f|
|
|
||||||
next unless File.file?(f)
|
|
||||||
|
|
||||||
target = File.join(to, f.gsub(/^#{Regexp.escape from}/, ''))
|
|
||||||
|
|
||||||
FileUtils.mkdir_p File.dirname(target)
|
|
||||||
msg "%20s => %-20s" % [f, target] if verbose?
|
|
||||||
FileUtils.cp f, target
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
CLI.new.run!
|
|
||||||
|
|
3224
src/json.hpp
3224
src/json.hpp
File diff suppressed because it is too large
Load diff
|
@ -78,7 +78,7 @@ test-%: src/unit-%.o src/unit.o ../src/json.hpp thirdparty/catch/catch.hpp
|
||||||
|
|
||||||
TEST_PATTERN ?= "*"
|
TEST_PATTERN ?= "*"
|
||||||
TEST_PREFIX = ""
|
TEST_PREFIX = ""
|
||||||
check: $(TESTCASES)
|
check: $(OBJECTS) $(TESTCASES)
|
||||||
@cd .. ; for testcase in $(TESTCASES); do echo "Executing $$testcase..."; $(TEST_PREFIX)test/$$testcase $(TEST_PATTERN) || exit 1; done
|
@cd .. ; for testcase in $(TESTCASES); do echo "Executing $$testcase..."; $(TEST_PREFIX)test/$$testcase $(TEST_PATTERN) || exit 1; done
|
||||||
|
|
||||||
|
|
||||||
|
|
75
test/data/json-patch-tests/README.md
Executable file
75
test/data/json-patch-tests/README.md
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
JSON Patch Tests
|
||||||
|
================
|
||||||
|
|
||||||
|
These are test cases for implementations of [IETF JSON Patch (RFC6902)](http://tools.ietf.org/html/rfc6902).
|
||||||
|
|
||||||
|
Some implementations can be found at [jsonpatch.com](http://jsonpatch.com).
|
||||||
|
|
||||||
|
|
||||||
|
Test Format
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Each test file is a JSON document that contains an array of test records. A
|
||||||
|
test record is an object with the following members:
|
||||||
|
|
||||||
|
- doc: The JSON document to test against
|
||||||
|
- patch: The patch(es) to apply
|
||||||
|
- expected: The expected resulting document, OR
|
||||||
|
- error: A string describing an expected error
|
||||||
|
- comment: A string describing the test
|
||||||
|
- disabled: True if the test should be skipped
|
||||||
|
|
||||||
|
All fields except 'doc' and 'patch' are optional. Test records consisting only
|
||||||
|
of a comment are also OK.
|
||||||
|
|
||||||
|
|
||||||
|
Files
|
||||||
|
-----
|
||||||
|
|
||||||
|
- tests.json: the main test file
|
||||||
|
- spec_tests.json: tests from the RFC6902 spec
|
||||||
|
|
||||||
|
|
||||||
|
Writing Tests
|
||||||
|
-------------
|
||||||
|
|
||||||
|
All tests should have a descriptive comment. Tests should be as
|
||||||
|
simple as possible - just what's required to test a specific piece of
|
||||||
|
behavior. If you want to test interacting behaviors, create tests for
|
||||||
|
each behavior as well as the interaction.
|
||||||
|
|
||||||
|
If an 'error' member is specified, the error text should describe the
|
||||||
|
error the implementation should raise - *not* what's being tested.
|
||||||
|
Implementation error strings will vary, but the suggested error should
|
||||||
|
be easily matched to the implementation error string. Try to avoid
|
||||||
|
creating error tests that might pass because an incorrect error was
|
||||||
|
reported.
|
||||||
|
|
||||||
|
Please feel free to contribute!
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
-------
|
||||||
|
|
||||||
|
The seed test set was adapted from Byron Ruth's
|
||||||
|
[jsonpatch-js](https://github.com/bruth/jsonpatch-js/blob/master/test.js) and
|
||||||
|
extended by [Mike McCabe](https://github.com/mikemccabe).
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
Copyright 2014 The Authors
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
233
test/data/json-patch-tests/spec_tests.json
Executable file
233
test/data/json-patch-tests/spec_tests.json
Executable file
|
@ -0,0 +1,233 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"comment": "4.1. add with missing object",
|
||||||
|
"doc": { "q": { "bar": 2 } },
|
||||||
|
"patch": [ {"op": "add", "path": "/a/b", "value": 1} ],
|
||||||
|
"error":
|
||||||
|
"path /a does not exist -- missing objects are not created recursively"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.1. Adding an Object Member",
|
||||||
|
"doc": {
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "add", "path": "/baz", "value": "qux" }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"baz": "qux",
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.2. Adding an Array Element",
|
||||||
|
"doc": {
|
||||||
|
"foo": [ "bar", "baz" ]
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "add", "path": "/foo/1", "value": "qux" }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"foo": [ "bar", "qux", "baz" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.3. Removing an Object Member",
|
||||||
|
"doc": {
|
||||||
|
"baz": "qux",
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "remove", "path": "/baz" }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.4. Removing an Array Element",
|
||||||
|
"doc": {
|
||||||
|
"foo": [ "bar", "qux", "baz" ]
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "remove", "path": "/foo/1" }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"foo": [ "bar", "baz" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.5. Replacing a Value",
|
||||||
|
"doc": {
|
||||||
|
"baz": "qux",
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "replace", "path": "/baz", "value": "boo" }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"baz": "boo",
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.6. Moving a Value",
|
||||||
|
"doc": {
|
||||||
|
"foo": {
|
||||||
|
"bar": "baz",
|
||||||
|
"waldo": "fred"
|
||||||
|
},
|
||||||
|
"qux": {
|
||||||
|
"corge": "grault"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"foo": {
|
||||||
|
"bar": "baz"
|
||||||
|
},
|
||||||
|
"qux": {
|
||||||
|
"corge": "grault",
|
||||||
|
"thud": "fred"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.7. Moving an Array Element",
|
||||||
|
"doc": {
|
||||||
|
"foo": [ "all", "grass", "cows", "eat" ]
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "move", "from": "/foo/1", "path": "/foo/3" }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"foo": [ "all", "cows", "eat", "grass" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.8. Testing a Value: Success",
|
||||||
|
"doc": {
|
||||||
|
"baz": "qux",
|
||||||
|
"foo": [ "a", 2, "c" ]
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "test", "path": "/baz", "value": "qux" },
|
||||||
|
{ "op": "test", "path": "/foo/1", "value": 2 }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"baz": "qux",
|
||||||
|
"foo": [ "a", 2, "c" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.9. Testing a Value: Error",
|
||||||
|
"doc": {
|
||||||
|
"baz": "qux"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "test", "path": "/baz", "value": "bar" }
|
||||||
|
],
|
||||||
|
"error": "string not equivalent"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.10. Adding a nested Member Object",
|
||||||
|
"doc": {
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "add", "path": "/child", "value": { "grandchild": { } } }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"foo": "bar",
|
||||||
|
"child": {
|
||||||
|
"grandchild": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.11. Ignoring Unrecognized Elements",
|
||||||
|
"doc": {
|
||||||
|
"foo":"bar"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }
|
||||||
|
],
|
||||||
|
"expected": {
|
||||||
|
"foo":"bar",
|
||||||
|
"baz":"qux"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.12. Adding to a Non-existent Target",
|
||||||
|
"doc": {
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "add", "path": "/baz/bat", "value": "qux" }
|
||||||
|
],
|
||||||
|
"error": "add to a non-existent target"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.13 Invalid JSON Patch Document",
|
||||||
|
"doc": {
|
||||||
|
"foo": "bar"
|
||||||
|
},
|
||||||
|
"patch": [
|
||||||
|
{ "op": "add", "path": "/baz", "value": "qux", "op": "remove" }
|
||||||
|
],
|
||||||
|
"error": "operation has two 'op' members",
|
||||||
|
"disabled": true
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.14. ~ Escape Ordering",
|
||||||
|
"doc": {
|
||||||
|
"/": 9,
|
||||||
|
"~1": 10
|
||||||
|
},
|
||||||
|
"patch": [{"op": "test", "path": "/~01", "value": 10}],
|
||||||
|
"expected": {
|
||||||
|
"/": 9,
|
||||||
|
"~1": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.15. Comparing Strings and Numbers",
|
||||||
|
"doc": {
|
||||||
|
"/": 9,
|
||||||
|
"~1": 10
|
||||||
|
},
|
||||||
|
"patch": [{"op": "test", "path": "/~01", "value": "10"}],
|
||||||
|
"error": "number is not equal to string"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"comment": "A.16. Adding an Array Value",
|
||||||
|
"doc": {
|
||||||
|
"foo": ["bar"]
|
||||||
|
},
|
||||||
|
"patch": [{ "op": "add", "path": "/foo/-", "value": ["abc", "def"] }],
|
||||||
|
"expected": {
|
||||||
|
"foo": ["bar", ["abc", "def"]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
434
test/data/json-patch-tests/tests.json
Executable file
434
test/data/json-patch-tests/tests.json
Executable file
|
@ -0,0 +1,434 @@
|
||||||
|
[
|
||||||
|
{ "comment": "empty list, empty docs",
|
||||||
|
"doc": {},
|
||||||
|
"patch": [],
|
||||||
|
"expected": {} },
|
||||||
|
|
||||||
|
{ "comment": "empty patch list",
|
||||||
|
"doc": {"foo": 1},
|
||||||
|
"patch": [],
|
||||||
|
"expected": {"foo": 1} },
|
||||||
|
|
||||||
|
{ "comment": "rearrangements OK?",
|
||||||
|
"doc": {"foo": 1, "bar": 2},
|
||||||
|
"patch": [],
|
||||||
|
"expected": {"bar":2, "foo": 1} },
|
||||||
|
|
||||||
|
{ "comment": "rearrangements OK? How about one level down ... array",
|
||||||
|
"doc": [{"foo": 1, "bar": 2}],
|
||||||
|
"patch": [],
|
||||||
|
"expected": [{"bar":2, "foo": 1}] },
|
||||||
|
|
||||||
|
{ "comment": "rearrangements OK? How about one level down...",
|
||||||
|
"doc": {"foo":{"foo": 1, "bar": 2}},
|
||||||
|
"patch": [],
|
||||||
|
"expected": {"foo":{"bar":2, "foo": 1}} },
|
||||||
|
|
||||||
|
{ "comment": "add replaces any existing field",
|
||||||
|
"doc": {"foo": null},
|
||||||
|
"patch": [{"op": "add", "path": "/foo", "value":1}],
|
||||||
|
"expected": {"foo": 1} },
|
||||||
|
|
||||||
|
{ "comment": "toplevel array",
|
||||||
|
"doc": [],
|
||||||
|
"patch": [{"op": "add", "path": "/0", "value": "foo"}],
|
||||||
|
"expected": ["foo"] },
|
||||||
|
|
||||||
|
{ "comment": "toplevel array, no change",
|
||||||
|
"doc": ["foo"],
|
||||||
|
"patch": [],
|
||||||
|
"expected": ["foo"] },
|
||||||
|
|
||||||
|
{ "comment": "toplevel object, numeric string",
|
||||||
|
"doc": {},
|
||||||
|
"patch": [{"op": "add", "path": "/foo", "value": "1"}],
|
||||||
|
"expected": {"foo":"1"} },
|
||||||
|
|
||||||
|
{ "comment": "toplevel object, integer",
|
||||||
|
"doc": {},
|
||||||
|
"patch": [{"op": "add", "path": "/foo", "value": 1}],
|
||||||
|
"expected": {"foo":1} },
|
||||||
|
|
||||||
|
{ "comment": "Toplevel scalar values OK?",
|
||||||
|
"doc": "foo",
|
||||||
|
"patch": [{"op": "replace", "path": "", "value": "bar"}],
|
||||||
|
"expected": "bar",
|
||||||
|
"disabled": true },
|
||||||
|
|
||||||
|
{ "comment": "replace object document with array document?",
|
||||||
|
"doc": {},
|
||||||
|
"patch": [{"op": "add", "path": "", "value": []}],
|
||||||
|
"expected": [] },
|
||||||
|
|
||||||
|
{ "comment": "replace array document with object document?",
|
||||||
|
"doc": [],
|
||||||
|
"patch": [{"op": "add", "path": "", "value": {}}],
|
||||||
|
"expected": {} },
|
||||||
|
|
||||||
|
{ "comment": "append to root array document?",
|
||||||
|
"doc": [],
|
||||||
|
"patch": [{"op": "add", "path": "/-", "value": "hi"}],
|
||||||
|
"expected": ["hi"] },
|
||||||
|
|
||||||
|
{ "comment": "Add, / target",
|
||||||
|
"doc": {},
|
||||||
|
"patch": [ {"op": "add", "path": "/", "value":1 } ],
|
||||||
|
"expected": {"":1} },
|
||||||
|
|
||||||
|
{ "comment": "Add, /foo/ deep target (trailing slash)",
|
||||||
|
"doc": {"foo": {}},
|
||||||
|
"patch": [ {"op": "add", "path": "/foo/", "value":1 } ],
|
||||||
|
"expected": {"foo":{"": 1}} },
|
||||||
|
|
||||||
|
{ "comment": "Add composite value at top level",
|
||||||
|
"doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "add", "path": "/bar", "value": [1, 2]}],
|
||||||
|
"expected": {"foo": 1, "bar": [1, 2]} },
|
||||||
|
|
||||||
|
{ "comment": "Add into composite value",
|
||||||
|
"doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||||
|
"patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}],
|
||||||
|
"expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} },
|
||||||
|
|
||||||
|
{ "doc": {"bar": [1, 2]},
|
||||||
|
"patch": [{"op": "add", "path": "/bar/8", "value": "5"}],
|
||||||
|
"error": "Out of bounds (upper)" },
|
||||||
|
|
||||||
|
{ "doc": {"bar": [1, 2]},
|
||||||
|
"patch": [{"op": "add", "path": "/bar/-1", "value": "5"}],
|
||||||
|
"error": "Out of bounds (lower)" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "add", "path": "/bar", "value": true}],
|
||||||
|
"expected": {"foo": 1, "bar": true} },
|
||||||
|
|
||||||
|
{ "doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "add", "path": "/bar", "value": false}],
|
||||||
|
"expected": {"foo": 1, "bar": false} },
|
||||||
|
|
||||||
|
{ "doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "add", "path": "/bar", "value": null}],
|
||||||
|
"expected": {"foo": 1, "bar": null} },
|
||||||
|
|
||||||
|
{ "comment": "0 can be an array index or object element name",
|
||||||
|
"doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "add", "path": "/0", "value": "bar"}],
|
||||||
|
"expected": {"foo": 1, "0": "bar" } },
|
||||||
|
|
||||||
|
{ "doc": ["foo"],
|
||||||
|
"patch": [{"op": "add", "path": "/1", "value": "bar"}],
|
||||||
|
"expected": ["foo", "bar"] },
|
||||||
|
|
||||||
|
{ "doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op": "add", "path": "/1", "value": "bar"}],
|
||||||
|
"expected": ["foo", "bar", "sil"] },
|
||||||
|
|
||||||
|
{ "doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op": "add", "path": "/0", "value": "bar"}],
|
||||||
|
"expected": ["bar", "foo", "sil"] },
|
||||||
|
|
||||||
|
{ "comment": "push item to array via last index + 1",
|
||||||
|
"doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op":"add", "path": "/2", "value": "bar"}],
|
||||||
|
"expected": ["foo", "sil", "bar"] },
|
||||||
|
|
||||||
|
{ "comment": "add item to array at index > length should fail",
|
||||||
|
"doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op":"add", "path": "/3", "value": "bar"}],
|
||||||
|
"error": "index is greater than number of items in array" },
|
||||||
|
|
||||||
|
{ "comment": "test against implementation-specific numeric parsing",
|
||||||
|
"doc": {"1e0": "foo"},
|
||||||
|
"patch": [{"op": "test", "path": "/1e0", "value": "foo"}],
|
||||||
|
"expected": {"1e0": "foo"} },
|
||||||
|
|
||||||
|
{ "comment": "test with bad number should fail",
|
||||||
|
"doc": ["foo", "bar"],
|
||||||
|
"patch": [{"op": "test", "path": "/1e0", "value": "bar"}],
|
||||||
|
"error": "test op shouldn't get array element 1" },
|
||||||
|
|
||||||
|
{ "doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op": "add", "path": "/bar", "value": 42}],
|
||||||
|
"error": "Object operation on array target" },
|
||||||
|
|
||||||
|
{ "doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}],
|
||||||
|
"expected": ["foo", ["bar", "baz"], "sil"],
|
||||||
|
"comment": "value in array add not flattened" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": 1, "bar": [1, 2, 3, 4]},
|
||||||
|
"patch": [{"op": "remove", "path": "/bar"}],
|
||||||
|
"expected": {"foo": 1} },
|
||||||
|
|
||||||
|
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||||
|
"patch": [{"op": "remove", "path": "/baz/0/qux"}],
|
||||||
|
"expected": {"foo": 1, "baz": [{}]} },
|
||||||
|
|
||||||
|
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||||
|
"patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}],
|
||||||
|
"expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} },
|
||||||
|
|
||||||
|
{ "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]},
|
||||||
|
"patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}],
|
||||||
|
"expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} },
|
||||||
|
|
||||||
|
{ "doc": ["foo"],
|
||||||
|
"patch": [{"op": "replace", "path": "/0", "value": "bar"}],
|
||||||
|
"expected": ["bar"] },
|
||||||
|
|
||||||
|
{ "doc": [""],
|
||||||
|
"patch": [{"op": "replace", "path": "/0", "value": 0}],
|
||||||
|
"expected": [0] },
|
||||||
|
|
||||||
|
{ "doc": [""],
|
||||||
|
"patch": [{"op": "replace", "path": "/0", "value": true}],
|
||||||
|
"expected": [true] },
|
||||||
|
|
||||||
|
{ "doc": [""],
|
||||||
|
"patch": [{"op": "replace", "path": "/0", "value": false}],
|
||||||
|
"expected": [false] },
|
||||||
|
|
||||||
|
{ "doc": [""],
|
||||||
|
"patch": [{"op": "replace", "path": "/0", "value": null}],
|
||||||
|
"expected": [null] },
|
||||||
|
|
||||||
|
{ "doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}],
|
||||||
|
"expected": ["foo", ["bar", "baz"]],
|
||||||
|
"comment": "value in array replace not flattened" },
|
||||||
|
|
||||||
|
{ "comment": "replace whole document",
|
||||||
|
"doc": {"foo": "bar"},
|
||||||
|
"patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}],
|
||||||
|
"expected": {"baz": "qux"} },
|
||||||
|
|
||||||
|
{ "comment": "test replace with missing parent key should fail",
|
||||||
|
"doc": {"bar": "baz"},
|
||||||
|
"patch": [{"op": "replace", "path": "/foo/bar", "value": false}],
|
||||||
|
"error": "replace op should fail with missing parent key" },
|
||||||
|
|
||||||
|
{ "comment": "spurious patch properties",
|
||||||
|
"doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}],
|
||||||
|
"expected": {"foo": 1} },
|
||||||
|
|
||||||
|
{ "doc": {"foo": null},
|
||||||
|
"patch": [{"op": "test", "path": "/foo", "value": null}],
|
||||||
|
"comment": "null value should be valid obj property" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": null},
|
||||||
|
"patch": [{"op": "replace", "path": "/foo", "value": "truthy"}],
|
||||||
|
"expected": {"foo": "truthy"},
|
||||||
|
"comment": "null value should be valid obj property to be replaced with something truthy" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": null},
|
||||||
|
"patch": [{"op": "move", "from": "/foo", "path": "/bar"}],
|
||||||
|
"expected": {"bar": null},
|
||||||
|
"comment": "null value should be valid obj property to be moved" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": null},
|
||||||
|
"patch": [{"op": "copy", "from": "/foo", "path": "/bar"}],
|
||||||
|
"expected": {"foo": null, "bar": null},
|
||||||
|
"comment": "null value should be valid obj property to be copied" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": null},
|
||||||
|
"patch": [{"op": "remove", "path": "/foo"}],
|
||||||
|
"expected": {},
|
||||||
|
"comment": "null value should be valid obj property to be removed" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": "bar"},
|
||||||
|
"patch": [{"op": "replace", "path": "/foo", "value": null}],
|
||||||
|
"expected": {"foo": null},
|
||||||
|
"comment": "null value should still be valid obj property replace other value" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": {"foo": 1, "bar": 2}},
|
||||||
|
"patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}],
|
||||||
|
"comment": "test should pass despite rearrangement" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": [{"foo": 1, "bar": 2}]},
|
||||||
|
"patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}],
|
||||||
|
"comment": "test should pass despite (nested) rearrangement" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": {"bar": [1, 2, 5, 4]}},
|
||||||
|
"patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}],
|
||||||
|
"comment": "test should pass - no error" },
|
||||||
|
|
||||||
|
{ "doc": {"foo": {"bar": [1, 2, 5, 4]}},
|
||||||
|
"patch": [{"op": "test", "path": "/foo", "value": [1, 2]}],
|
||||||
|
"error": "test op should fail" },
|
||||||
|
|
||||||
|
{ "comment": "Whole document",
|
||||||
|
"doc": { "foo": 1 },
|
||||||
|
"patch": [{"op": "test", "path": "", "value": {"foo": 1}}],
|
||||||
|
"disabled": true },
|
||||||
|
|
||||||
|
{ "comment": "Empty-string element",
|
||||||
|
"doc": { "": 1 },
|
||||||
|
"patch": [{"op": "test", "path": "/", "value": 1}] },
|
||||||
|
|
||||||
|
{ "doc": {
|
||||||
|
"foo": ["bar", "baz"],
|
||||||
|
"": 0,
|
||||||
|
"a/b": 1,
|
||||||
|
"c%d": 2,
|
||||||
|
"e^f": 3,
|
||||||
|
"g|h": 4,
|
||||||
|
"i\\j": 5,
|
||||||
|
"k\"l": 6,
|
||||||
|
" ": 7,
|
||||||
|
"m~n": 8
|
||||||
|
},
|
||||||
|
"patch": [{"op": "test", "path": "/foo", "value": ["bar", "baz"]},
|
||||||
|
{"op": "test", "path": "/foo/0", "value": "bar"},
|
||||||
|
{"op": "test", "path": "/", "value": 0},
|
||||||
|
{"op": "test", "path": "/a~1b", "value": 1},
|
||||||
|
{"op": "test", "path": "/c%d", "value": 2},
|
||||||
|
{"op": "test", "path": "/e^f", "value": 3},
|
||||||
|
{"op": "test", "path": "/g|h", "value": 4},
|
||||||
|
{"op": "test", "path": "/i\\j", "value": 5},
|
||||||
|
{"op": "test", "path": "/k\"l", "value": 6},
|
||||||
|
{"op": "test", "path": "/ ", "value": 7},
|
||||||
|
{"op": "test", "path": "/m~0n", "value": 8}] },
|
||||||
|
|
||||||
|
{ "comment": "Move to same location has no effect",
|
||||||
|
"doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "move", "from": "/foo", "path": "/foo"}],
|
||||||
|
"expected": {"foo": 1} },
|
||||||
|
|
||||||
|
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||||
|
"patch": [{"op": "move", "from": "/foo", "path": "/bar"}],
|
||||||
|
"expected": {"baz": [{"qux": "hello"}], "bar": 1} },
|
||||||
|
|
||||||
|
{ "doc": {"baz": [{"qux": "hello"}], "bar": 1},
|
||||||
|
"patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}],
|
||||||
|
"expected": {"baz": [{}, "hello"], "bar": 1} },
|
||||||
|
|
||||||
|
{ "doc": {"baz": [{"qux": "hello"}], "bar": 1},
|
||||||
|
"patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}],
|
||||||
|
"expected": {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}} },
|
||||||
|
|
||||||
|
{ "comment": "replacing the root of the document is possible with add",
|
||||||
|
"doc": {"foo": "bar"},
|
||||||
|
"patch": [{"op": "add", "path": "", "value": {"baz": "qux"}}],
|
||||||
|
"expected": {"baz":"qux"}},
|
||||||
|
|
||||||
|
{ "comment": "Adding to \"/-\" adds to the end of the array",
|
||||||
|
"doc": [ 1, 2 ],
|
||||||
|
"patch": [ { "op": "add", "path": "/-", "value": { "foo": [ "bar", "baz" ] } } ],
|
||||||
|
"expected": [ 1, 2, { "foo": [ "bar", "baz" ] } ]},
|
||||||
|
|
||||||
|
{ "comment": "Adding to \"/-\" adds to the end of the array, even n levels down",
|
||||||
|
"doc": [ 1, 2, [ 3, [ 4, 5 ] ] ],
|
||||||
|
"patch": [ { "op": "add", "path": "/2/1/-", "value": { "foo": [ "bar", "baz" ] } } ],
|
||||||
|
"expected": [ 1, 2, [ 3, [ 4, 5, { "foo": [ "bar", "baz" ] } ] ] ]},
|
||||||
|
|
||||||
|
{ "comment": "test remove with bad number should fail",
|
||||||
|
"doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||||
|
"patch": [{"op": "remove", "path": "/baz/1e0/qux"}],
|
||||||
|
"error": "remove op shouldn't remove from array with bad number" },
|
||||||
|
|
||||||
|
{ "comment": "test remove on array",
|
||||||
|
"doc": [1, 2, 3, 4],
|
||||||
|
"patch": [{"op": "remove", "path": "/0"}],
|
||||||
|
"expected": [2, 3, 4] },
|
||||||
|
|
||||||
|
{ "comment": "test repeated removes",
|
||||||
|
"doc": [1, 2, 3, 4],
|
||||||
|
"patch": [{ "op": "remove", "path": "/1" },
|
||||||
|
{ "op": "remove", "path": "/2" }],
|
||||||
|
"expected": [1, 3] },
|
||||||
|
|
||||||
|
{ "comment": "test remove with bad index should fail",
|
||||||
|
"doc": [1, 2, 3, 4],
|
||||||
|
"patch": [{"op": "remove", "path": "/1e0"}],
|
||||||
|
"error": "remove op shouldn't remove from array with bad number" },
|
||||||
|
|
||||||
|
{ "comment": "test replace with bad number should fail",
|
||||||
|
"doc": [""],
|
||||||
|
"patch": [{"op": "replace", "path": "/1e0", "value": false}],
|
||||||
|
"error": "replace op shouldn't replace in array with bad number" },
|
||||||
|
|
||||||
|
{ "comment": "test copy with bad number should fail",
|
||||||
|
"doc": {"baz": [1,2,3], "bar": 1},
|
||||||
|
"patch": [{"op": "copy", "from": "/baz/1e0", "path": "/boo"}],
|
||||||
|
"error": "copy op shouldn't work with bad number" },
|
||||||
|
|
||||||
|
{ "comment": "test move with bad number should fail",
|
||||||
|
"doc": {"foo": 1, "baz": [1,2,3,4]},
|
||||||
|
"patch": [{"op": "move", "from": "/baz/1e0", "path": "/foo"}],
|
||||||
|
"error": "move op shouldn't work with bad number" },
|
||||||
|
|
||||||
|
{ "comment": "test add with bad number should fail",
|
||||||
|
"doc": ["foo", "sil"],
|
||||||
|
"patch": [{"op": "add", "path": "/1e0", "value": "bar"}],
|
||||||
|
"error": "add op shouldn't add to array with bad number" },
|
||||||
|
|
||||||
|
{ "comment": "missing 'value' parameter to add",
|
||||||
|
"doc": [ 1 ],
|
||||||
|
"patch": [ { "op": "add", "path": "/-" } ],
|
||||||
|
"error": "missing 'value' parameter" },
|
||||||
|
|
||||||
|
{ "comment": "missing 'value' parameter to replace",
|
||||||
|
"doc": [ 1 ],
|
||||||
|
"patch": [ { "op": "replace", "path": "/0" } ],
|
||||||
|
"error": "missing 'value' parameter" },
|
||||||
|
|
||||||
|
{ "comment": "missing 'value' parameter to test",
|
||||||
|
"doc": [ null ],
|
||||||
|
"patch": [ { "op": "test", "path": "/0" } ],
|
||||||
|
"error": "missing 'value' parameter" },
|
||||||
|
|
||||||
|
{ "comment": "missing value parameter to test - where undef is falsy",
|
||||||
|
"doc": [ false ],
|
||||||
|
"patch": [ { "op": "test", "path": "/0" } ],
|
||||||
|
"error": "missing 'value' parameter" },
|
||||||
|
|
||||||
|
{ "comment": "missing from parameter to copy",
|
||||||
|
"doc": [ 1 ],
|
||||||
|
"patch": [ { "op": "copy", "path": "/-" } ],
|
||||||
|
"error": "missing 'from' parameter" },
|
||||||
|
|
||||||
|
{ "comment": "missing from parameter to move",
|
||||||
|
"doc": { "foo": 1 },
|
||||||
|
"patch": [ { "op": "move", "path": "" } ],
|
||||||
|
"error": "missing 'from' parameter" },
|
||||||
|
|
||||||
|
{ "comment": "duplicate ops",
|
||||||
|
"doc": { "foo": "bar" },
|
||||||
|
"patch": [ { "op": "add", "path": "/baz", "value": "qux",
|
||||||
|
"op": "move", "from":"/foo" } ],
|
||||||
|
"error": "patch has two 'op' members",
|
||||||
|
"disabled": true },
|
||||||
|
|
||||||
|
{ "comment": "unrecognized op should fail",
|
||||||
|
"doc": {"foo": 1},
|
||||||
|
"patch": [{"op": "spam", "path": "/foo", "value": 1}],
|
||||||
|
"error": "Unrecognized op 'spam'" },
|
||||||
|
|
||||||
|
{ "comment": "test with bad array number that has leading zeros",
|
||||||
|
"doc": ["foo", "bar"],
|
||||||
|
"patch": [{"op": "test", "path": "/00", "value": "foo"}],
|
||||||
|
"error": "test op should reject the array value, it has leading zeros" },
|
||||||
|
|
||||||
|
{ "comment": "test with bad array number that has leading zeros",
|
||||||
|
"doc": ["foo", "bar"],
|
||||||
|
"patch": [{"op": "test", "path": "/01", "value": "bar"}],
|
||||||
|
"error": "test op should reject the array value, it has leading zeros" },
|
||||||
|
|
||||||
|
{ "comment": "Removing nonexistent field",
|
||||||
|
"doc": {"foo" : "bar"},
|
||||||
|
"patch": [{"op": "remove", "path": "/baz"}],
|
||||||
|
"error": "removing a nonexistent field should fail" },
|
||||||
|
|
||||||
|
{ "comment": "Removing nonexistent index",
|
||||||
|
"doc": ["foo", "bar"],
|
||||||
|
"patch": [{"op": "remove", "path": "/2"}],
|
||||||
|
"error": "removing a nonexistent index should fail" },
|
||||||
|
|
||||||
|
{ "comment": "Patch with different capitalisation than doc",
|
||||||
|
"doc": {"foo":"bar"},
|
||||||
|
"patch": [{"op": "add", "path": "/FOO", "value": "BAR"}],
|
||||||
|
"expected": {"foo": "bar", "FOO": "BAR"}
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
BIN
test/data/markus_kuhn/UTF-8-test.txt
Normal file
BIN
test/data/markus_kuhn/UTF-8-test.txt
Normal file
Binary file not shown.
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
__ _____ _____ _____
|
__ _____ _____ _____
|
||||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||||
| | |__ | | | | | | version 2.1.1
|
| | |__ | | | | | | version 3.0.1
|
||||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on
|
This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
__ _____ _____ _____
|
__ _____ _____ _____
|
||||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||||
| | |__ | | | | | | version 2.1.1
|
| | |__ | | | | | | version 3.0.1
|
||||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
__ _____ _____ _____
|
__ _____ _____ _____
|
||||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||||
| | |__ | | | | | | version 2.1.1
|
| | |__ | | | | | | version 3.0.1
|
||||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
__ _____ _____ _____
|
__ _____ _____ _____
|
||||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||||
| | |__ | | | | | | version 2.1.1
|
| | |__ | | | | | | version 3.0.1
|
||||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
__ _____ _____ _____
|
__ _____ _____ _____
|
||||||
__| | __| | | | JSON for Modern C++ (test suite)
|
__| | __| | | | JSON for Modern C++ (test suite)
|
||||||
| | |__ | | | | | | version 2.1.1
|
| | |__ | | | | | | version 3.0.1
|
||||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
@ -256,7 +256,6 @@ TEST_CASE("algorithms")
|
||||||
|
|
||||||
SECTION("set operations")
|
SECTION("set operations")
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
SECTION("std::merge")
|
SECTION("std::merge")
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -268,7 +267,6 @@ TEST_CASE("algorithms")
|
||||||
CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8}));
|
CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
SECTION("std::set_difference")
|
SECTION("std::set_difference")
|
||||||
{
|
{
|
||||||
|
@ -290,7 +288,6 @@ TEST_CASE("algorithms")
|
||||||
CHECK(j3 == json({1, 2, 3, 5, 7}));
|
CHECK(j3 == json({1, 2, 3, 5, 7}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
SECTION("std::set_union")
|
SECTION("std::set_union")
|
||||||
{
|
{
|
||||||
json j1 = {2, 4, 6, 8};
|
json j1 = {2, 4, 6, 8};
|
||||||
|
@ -310,7 +307,6 @@ TEST_CASE("algorithms")
|
||||||
std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
|
std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
|
||||||
CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8}));
|
CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8}));
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("heap operations")
|
SECTION("heap operations")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
__ _____ _____ _____
|
__ _____ _____ _____
|
||||||
__| | __| | | | JSON for Modern C++ (test suite)
|
__| | __| | | | JSON for Modern C++ (test suite)
|
||||||
| | |__ | | | | | | version 2.1.1
|
| | |__ | | | | | | version 3.0.1
|
||||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
|
||||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue