cleanup
This commit is contained in:
parent
b453cdb7a2
commit
befd90dead
15 changed files with 4702 additions and 11586 deletions
|
@ -1,61 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 2.8.4)
|
|
||||||
project(json)
|
|
||||||
|
|
||||||
# Enable C++11 and set flags for coverage testing
|
|
||||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -O0 --coverage -fprofile-arcs -ftest-coverage")
|
|
||||||
|
|
||||||
# Make everything public for testing purposes
|
|
||||||
add_definitions(-Dprivate=public)
|
|
||||||
|
|
||||||
# If not specified, use Debug as build type (necessary for coverage testing)
|
|
||||||
if( NOT CMAKE_BUILD_TYPE )
|
|
||||||
set( CMAKE_BUILD_TYPE Debug CACHE STRING
|
|
||||||
""
|
|
||||||
FORCE )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# CMake addons for lcov
|
|
||||||
# Only possible with g++ at the moment. We run otherwise just the test
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage -fprofile-arcs -ftest-coverage")
|
|
||||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
|
|
||||||
include(CodeCoverage)
|
|
||||||
|
|
||||||
setup_coverage(coverage)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Normal sources
|
|
||||||
include_directories(src/)
|
|
||||||
aux_source_directory(src/ json_list)
|
|
||||||
add_library(json ${json_list})
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
# Search all test files in the test directory with a .cc suffix
|
|
||||||
file(GLOB TEST_FILES "test/*.cc")
|
|
||||||
foreach(TEST_FILE ${TEST_FILES})
|
|
||||||
# We use the basename to identify the test. E.g "json_unit" for "json_unit.cc"
|
|
||||||
get_filename_component(BASENAME ${TEST_FILE} NAME_WE)
|
|
||||||
# Create a test executable
|
|
||||||
add_executable(${BASENAME} ${TEST_FILE})
|
|
||||||
# Link it with our main json file
|
|
||||||
target_link_libraries(${BASENAME} json)
|
|
||||||
|
|
||||||
# Add test if people want to use ctest
|
|
||||||
add_test(${BASENAME} ${BASENAME})
|
|
||||||
|
|
||||||
# If we are using g++, we also need to setup the commands for coverage
|
|
||||||
# testing
|
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
# Add a run_XXX target that runs the executable and produces the
|
|
||||||
# coverage data automatically
|
|
||||||
add_custom_target(run_${BASENAME} COMMAND ./${BASENAME})
|
|
||||||
# Make sure that running requires the executable to be build
|
|
||||||
add_dependencies (run_${BASENAME} ${BASENAME})
|
|
||||||
# To create a valid coverage report, the executable has to be
|
|
||||||
# executed first
|
|
||||||
add_dependencies (coverage run_${BASENAME})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# 2012-01-31, Lars Bilke
|
|
||||||
# - Enable Code Coverage
|
|
||||||
#
|
|
||||||
# 2013-09-17, Joakim Söderberg
|
|
||||||
# - Added support for Clang.
|
|
||||||
# - Some additional usage instructions.
|
|
||||||
#
|
|
||||||
# USAGE:
|
|
||||||
|
|
||||||
# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here:
|
|
||||||
# http://stackoverflow.com/a/22404544/80480
|
|
||||||
#
|
|
||||||
# 1. Copy this file into your cmake modules path.
|
|
||||||
#
|
|
||||||
# 2. Add the following line to your CMakeLists.txt:
|
|
||||||
# INCLUDE(CodeCoverage)
|
|
||||||
#
|
|
||||||
# 3. Set compiler flags to turn off optimization and enable coverage:
|
|
||||||
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
|
||||||
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
|
|
||||||
#
|
|
||||||
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
|
|
||||||
# which runs your test executable and produces a lcov code coverage report:
|
|
||||||
# Example:
|
|
||||||
# SETUP_TARGET_FOR_COVERAGE(
|
|
||||||
# my_coverage_target # Name for custom target.
|
|
||||||
# test_driver # Name of the test driver executable that runs the tests.
|
|
||||||
# # NOTE! This should always have a ZERO as exit code
|
|
||||||
# # otherwise the coverage generation will not complete.
|
|
||||||
# coverage # Name of output directory.
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# 4. Build a Debug build:
|
|
||||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
|
||||||
# make
|
|
||||||
# make my_coverage_target
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
# Check prereqs
|
|
||||||
FIND_PROGRAM( GCOV_PATH gcov )
|
|
||||||
FIND_PROGRAM( LCOV_PATH lcov )
|
|
||||||
FIND_PROGRAM( GENHTML_PATH genhtml )
|
|
||||||
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
|
|
||||||
|
|
||||||
IF(NOT GCOV_PATH)
|
|
||||||
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
|
|
||||||
ENDIF() # NOT GCOV_PATH
|
|
||||||
|
|
||||||
IF(NOT CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
# Clang version 3.0.0 and greater now supports gcov as well.
|
|
||||||
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
|
|
||||||
|
|
||||||
IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
|
||||||
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
|
|
||||||
ENDIF()
|
|
||||||
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
|
|
||||||
|
|
||||||
SET(CMAKE_CXX_FLAGS_COVERAGE
|
|
||||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
|
||||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
|
||||||
FORCE )
|
|
||||||
SET(CMAKE_C_FLAGS_COVERAGE
|
|
||||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage"
|
|
||||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
|
||||||
FORCE )
|
|
||||||
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
|
||||||
""
|
|
||||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
|
||||||
FORCE )
|
|
||||||
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
|
||||||
""
|
|
||||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
|
||||||
FORCE )
|
|
||||||
MARK_AS_ADVANCED(
|
|
||||||
CMAKE_CXX_FLAGS_COVERAGE
|
|
||||||
CMAKE_C_FLAGS_COVERAGE
|
|
||||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
|
||||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
|
||||||
|
|
||||||
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
|
|
||||||
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
|
|
||||||
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
|
|
||||||
|
|
||||||
|
|
||||||
# Param _outputname lcov output is generated as _outputname.info
|
|
||||||
# HTML report is generated in _outputname/index.html
|
|
||||||
FUNCTION(SETUP_COVERAGE _outputname)
|
|
||||||
|
|
||||||
IF(NOT LCOV_PATH)
|
|
||||||
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
|
|
||||||
ENDIF() # NOT LCOV_PATH
|
|
||||||
|
|
||||||
IF(NOT GENHTML_PATH)
|
|
||||||
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
|
|
||||||
ENDIF() # NOT GENHTML_PATH
|
|
||||||
|
|
||||||
# Setup target
|
|
||||||
ADD_CUSTOM_TARGET(coverage
|
|
||||||
|
|
||||||
|
|
||||||
# Capturing lcov counters and generating report
|
|
||||||
COMMAND ${LCOV_PATH} --rc lcov_branch_coverage=1 --directory . --capture --output-file ${_outputname}.info
|
|
||||||
COMMAND ${LCOV_PATH} --rc lcov_branch_coverage=1 --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
|
|
||||||
COMMAND ${GENHTML_PATH} --branch-coverage --rc lcov_branch_coverage=1 -o ${_outputname} ${_outputname}.info.cleaned
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
|
|
||||||
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
|
||||||
# Cleanup lcov
|
|
||||||
${LCOV_PATH} --directory . --zerocounters
|
|
||||||
)
|
|
||||||
|
|
||||||
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
|
|
21
Makefile.am
21
Makefile.am
|
@ -1,20 +1,13 @@
|
||||||
.PHONY: header_only
|
.PHONY: header_only
|
||||||
|
|
||||||
# the order is important: header before other sources
|
noinst_PROGRAMS = json_unit
|
||||||
CORE_SOURCES = src/json.h src/json.cc
|
|
||||||
|
|
||||||
noinst_PROGRAMS = json_unit json_parser
|
|
||||||
|
|
||||||
FLAGS = -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder
|
FLAGS = -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder
|
||||||
|
|
||||||
json_unit_SOURCES = $(CORE_SOURCES) test/catch.hpp test/json_unit.cc
|
json_unit_SOURCES = $(CORE_SOURCES) test/catch.hpp test/unit.cpp src/json.hpp
|
||||||
json_unit_CXXFLAGS = $(FLAGS) -std=c++11
|
json_unit_CXXFLAGS = $(FLAGS) -std=c++11
|
||||||
json_unit_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/test -Dprivate=public
|
json_unit_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/test -Dprivate=public
|
||||||
|
|
||||||
json_parser_SOURCES = $(CORE_SOURCES) benchmark/parse.cc
|
|
||||||
json_parser_CXXFLAGS = -O3 -std=c++11 -flto
|
|
||||||
json_parser_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/benchmark
|
|
||||||
|
|
||||||
cppcheck:
|
cppcheck:
|
||||||
cppcheck --enable=all --inconclusive --std=c++11 src/json.*
|
cppcheck --enable=all --inconclusive --std=c++11 src/json.*
|
||||||
|
|
||||||
|
@ -29,13 +22,3 @@ pretty:
|
||||||
--align-reference=type --add-brackets --convert-tabs --close-templates \
|
--align-reference=type --add-brackets --convert-tabs --close-templates \
|
||||||
--lineend=linux --preserve-date --suffix=none \
|
--lineend=linux --preserve-date --suffix=none \
|
||||||
$(SOURCES)
|
$(SOURCES)
|
||||||
|
|
||||||
parser:
|
|
||||||
make CXXFLAGS="" json_parser
|
|
||||||
|
|
||||||
header_only/json.h: $(CORE_SOURCES)
|
|
||||||
$(AM_V_GEN)
|
|
||||||
$(AM_V_at)mkdir -p header_only
|
|
||||||
$(AM_V_at)cat $(CORE_SOURCES) | $(SED) 's/#include "json.h"//' > header_only/json.h
|
|
||||||
|
|
||||||
BUILT_SOURCES = header_only/json.h
|
|
||||||
|
|
383
README.md
383
README.md
|
@ -1,383 +0,0 @@
|
||||||
# JSON for Modern C++
|
|
||||||
|
|
||||||
*What if JSON was part of modern C++?*
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/nlohmann/json.png?branch=master)](https://travis-ci.org/nlohmann/json)
|
|
||||||
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
|
|
||||||
[![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues)
|
|
||||||
|
|
||||||
## Design goals
|
|
||||||
|
|
||||||
There are myriads of [JSON](http://json.org) libraries out there, and each may even have its reason to exist. Our class had these design goals:
|
|
||||||
|
|
||||||
- **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and the [reference](https://github.com/nlohmann/json/blob/master/doc/Reference.md), and you know, what I mean.
|
|
||||||
|
|
||||||
- **Trivial integration**. Our whole code consists of a class in just two files: A header file `json.h` and a source file `json.cc`. That's it. No library, no subproject, no dependencies. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
|
|
||||||
|
|
||||||
- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/json_unit.cc) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks.
|
|
||||||
|
|
||||||
Other aspects were not so important to us:
|
|
||||||
|
|
||||||
- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). We use the following C++ data types: `std::string` for strings, `int64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. We know that there are more efficient ways to store the values, but we are happy enough right now.
|
|
||||||
|
|
||||||
- **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) with a decent regular expression processor should be even faster (but would consist of more files which makes the integration harder).
|
|
||||||
|
|
||||||
- **Rigorous standard compliance**. Any [compliant](http://json.org) JSON file can be read by our class, and any output of the class is standard-compliant. However, we do not check for some details in the format of numbers and strings. For instance, `-0` will be treated as `0` whereas the standard forbids this. Furthermore, we allow for more escape symbols in strings as the JSON specification. While this may not be a problem in reality, we are aware of it, but as long as we have a hand-written parser, we won't invest too much to be fully compliant.
|
|
||||||
|
|
||||||
## Integration
|
|
||||||
|
|
||||||
The two required source files are in the `src` directory. All you need to do is add
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include "json.h"
|
|
||||||
|
|
||||||
// for convenience
|
|
||||||
using json = nlohmann::json;
|
|
||||||
```
|
|
||||||
|
|
||||||
to the files you want to use JSON objects. Furthermore, you need to compile the file `json.cc` and link it to your binaries. Do not forget to set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang).
|
|
||||||
|
|
||||||
If you want a single header file, use the `json.h` file from the `header_only` directory.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Here are some examples to give you an idea how to use the class.
|
|
||||||
|
|
||||||
Assume you want to create the JSON object
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pi": 3.141,
|
|
||||||
"happy": true,
|
|
||||||
"name": "Niels",
|
|
||||||
"nothing": null,
|
|
||||||
"answer": {
|
|
||||||
"everything": 42
|
|
||||||
},
|
|
||||||
"list": [1, 0, 2],
|
|
||||||
"object": {
|
|
||||||
"currency": "USD",
|
|
||||||
"value": 42.99
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
With the JSON class, you could write:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// create an empty structure (null)
|
|
||||||
json j;
|
|
||||||
|
|
||||||
// add a number that is stored as double (note the implicit conversion of j to an object)
|
|
||||||
j["pi"] = 3.141;
|
|
||||||
|
|
||||||
// add a Boolean that is stored as bool
|
|
||||||
j["happy"] = true;
|
|
||||||
|
|
||||||
// add a string that is stored as std::string
|
|
||||||
j["name"] = "Niels";
|
|
||||||
|
|
||||||
// add another null object by passing nullptr
|
|
||||||
j["nothing"] = nullptr;
|
|
||||||
|
|
||||||
// add an object inside the object
|
|
||||||
j["answer"]["everything"] = 42;
|
|
||||||
|
|
||||||
// add an array that is stored as std::vector (using an initializer list)
|
|
||||||
j["list"] = { 1, 0, 2 };
|
|
||||||
|
|
||||||
// add another object (using an initializer list of pairs)
|
|
||||||
j["object"] = { {"currency", "USD"}, {"value", 42.99} };
|
|
||||||
|
|
||||||
// instead, you could also write (which looks very similar to the JSON above)
|
|
||||||
json j2 = {
|
|
||||||
{"pi", 3.141},
|
|
||||||
{"happy", true},
|
|
||||||
{"name", "Niels"},
|
|
||||||
{"nothing", nullptr},
|
|
||||||
{"answer", {
|
|
||||||
{"everything", 42}
|
|
||||||
}},
|
|
||||||
{"list", {1, 0, 2}},
|
|
||||||
{"object", {
|
|
||||||
{"currency", "USD"},
|
|
||||||
{"value", 42.99}
|
|
||||||
}}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that in all theses cases, you never need to "tell" the compiler which JSON value you want to use. If you want to be explicit or express some edge cases, the functions `json::array` and `json::object` will help:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// ways to express the empty array []
|
|
||||||
json empty_array_implicit = {{}};
|
|
||||||
json empty_array_explicit = json::array();
|
|
||||||
|
|
||||||
// a way to express the empty object {}
|
|
||||||
json empty_object_explicit = json::object();
|
|
||||||
|
|
||||||
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
|
|
||||||
json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) };
|
|
||||||
```
|
|
||||||
|
|
||||||
### Serialization / Deserialization
|
|
||||||
|
|
||||||
You can create an object (deserialization) by appending `_json` to a string literal:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// create object from string literal
|
|
||||||
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
|
|
||||||
|
|
||||||
// or even nicer (thanks http://isocpp.org/blog/2015/01/json-for-modern-cpp)
|
|
||||||
auto j2 = R"(
|
|
||||||
{
|
|
||||||
"happy": true,
|
|
||||||
"pi": 3.141
|
|
||||||
}
|
|
||||||
)"_json;
|
|
||||||
|
|
||||||
// or explicitly
|
|
||||||
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also get a string representation (serialize):
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// explicit conversion to string
|
|
||||||
std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141}
|
|
||||||
|
|
||||||
// serialization with pretty printing
|
|
||||||
std::cout << j.dump(4) << std::endl;
|
|
||||||
// {
|
|
||||||
// "happy": true,
|
|
||||||
// "pi": 3.141
|
|
||||||
// }
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use streams to serialize and deserialize:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// deserialize from standard input
|
|
||||||
json j;
|
|
||||||
j << std::cin;
|
|
||||||
|
|
||||||
// serialize to standard output
|
|
||||||
std::cout << j;
|
|
||||||
```
|
|
||||||
|
|
||||||
These operators work for any subclasses of `std::istream` or `std::ostream`.
|
|
||||||
|
|
||||||
### STL-like access
|
|
||||||
|
|
||||||
We designed the JSON class to behave just like an STL container:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// create an array using push_back
|
|
||||||
json j;
|
|
||||||
j.push_back("foo");
|
|
||||||
j.push_back(1);
|
|
||||||
j.push_back(true);
|
|
||||||
|
|
||||||
// iterate the array
|
|
||||||
for (json::iterator it = j.begin(); it != j.end(); ++it) {
|
|
||||||
std::cout << *it << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
// range-based for
|
|
||||||
for (auto element : j) {
|
|
||||||
std::cout << element << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
// getter/setter
|
|
||||||
const std::string tmp = j[0];
|
|
||||||
j[1] = 42;
|
|
||||||
bool foo = j.at(2);
|
|
||||||
|
|
||||||
// other stuff
|
|
||||||
j.size(); // 3 entries
|
|
||||||
j.empty(); // false
|
|
||||||
j.type(); // json::value_t::array
|
|
||||||
j.clear(); // the array is empty again
|
|
||||||
|
|
||||||
// comparison
|
|
||||||
j == "[\"foo\", 1, true]"_json; // true
|
|
||||||
|
|
||||||
// create an object
|
|
||||||
json o;
|
|
||||||
o["foo"] = 23;
|
|
||||||
o["bar"] = false;
|
|
||||||
o["baz"] = 3.141;
|
|
||||||
|
|
||||||
// find an entry
|
|
||||||
if (o.find("foo") != o.end()) {
|
|
||||||
// there is an entry with key "foo"
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate the object
|
|
||||||
for (json::iterator it = o.begin(); it != o.end(); ++it) {
|
|
||||||
std::cout << it.key() << ':' << it.value() << '\n';
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Conversion from STL containers
|
|
||||||
|
|
||||||
Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON types (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends how the elements are ordered in the respective STL container.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::vector<int> c_vector {1, 2, 3, 4};
|
|
||||||
json j_vec(c_vector);
|
|
||||||
// [1, 2, 3, 4]
|
|
||||||
|
|
||||||
std::deque<float> c_deque {1.2, 2.3, 3.4, 5.6};
|
|
||||||
json j_deque(c_deque);
|
|
||||||
// [1.2, 2.3, 3.4, 5.6]
|
|
||||||
|
|
||||||
std::list<bool> c_list {true, true, false, true};
|
|
||||||
json j_list(c_list);
|
|
||||||
// [true, true, false, true]
|
|
||||||
|
|
||||||
std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
|
|
||||||
json j_flist(c_flist);
|
|
||||||
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
|
|
||||||
|
|
||||||
std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
|
|
||||||
json j_array(c_array);
|
|
||||||
// [1, 2, 3, 4]
|
|
||||||
|
|
||||||
std::set<std::string> c_set {"one", "two", "three", "four", "one"};
|
|
||||||
json j_set(c_set); // only one entry for "one" is used
|
|
||||||
// ["four", "one", "three", "two"]
|
|
||||||
|
|
||||||
std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
|
|
||||||
json j_uset(c_uset); // only one entry for "one" is used
|
|
||||||
// maybe ["two", "three", "four", "one"]
|
|
||||||
|
|
||||||
std::multiset<std::string> c_mset {"one", "two", "one", "four"};
|
|
||||||
json j_mset(c_mset); // only one entry for "one" is used
|
|
||||||
// maybe ["one", "two", "four"]
|
|
||||||
|
|
||||||
std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
|
|
||||||
json j_umset(c_umset); // both entries for "one" are used
|
|
||||||
// maybe ["one", "two", "one", "four"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys are can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
|
|
||||||
json j_map(c_map);
|
|
||||||
// {"one": 1, "two": 2, "three": 3}
|
|
||||||
|
|
||||||
std::unordered_map<const char*, float> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
|
|
||||||
json j_umap(c_umap);
|
|
||||||
// {"one": 1.2, "two": 2.3, "three": 3.4}
|
|
||||||
|
|
||||||
std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
|
|
||||||
json j_mmap(c_mmap); // only one entry for key "three" is used
|
|
||||||
// maybe {"one": true, "two": true, "three": true}
|
|
||||||
|
|
||||||
std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
|
|
||||||
json j_ummap(c_ummap); // only one entry for key "three" is used
|
|
||||||
// maybe {"one": true, "two": true, "three": true}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Implicit conversions
|
|
||||||
|
|
||||||
The type of the JSON object is determined automatically by the expression to store. Likewise, the stored value is implicitly converted.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
/// strings
|
|
||||||
std::string s1 = "Hello, world!";
|
|
||||||
json js = s1;
|
|
||||||
std::string s2 = js;
|
|
||||||
|
|
||||||
// Booleans
|
|
||||||
bool b1 = true;
|
|
||||||
json jb = b1;
|
|
||||||
bool b2 = jb;
|
|
||||||
|
|
||||||
// numbers
|
|
||||||
int i = 42;
|
|
||||||
json jn = i;
|
|
||||||
double f = jn;
|
|
||||||
|
|
||||||
// etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also explicitly ask for the value:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::string vs = js.get<std::string>();
|
|
||||||
bool vb = jb.get<bool>();
|
|
||||||
int vi = jn.get<int>();
|
|
||||||
|
|
||||||
// etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
<img align="right" src="http://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png">
|
|
||||||
|
|
||||||
The class is licensed under the [MIT License](http://opensource.org/licenses/MIT):
|
|
||||||
|
|
||||||
Copyright © 2013-2014 [Niels Lohmann](http://nlohmann.me)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
## Thanks
|
|
||||||
|
|
||||||
I deeply appreciate the help of the following people.
|
|
||||||
|
|
||||||
- [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.
|
|
||||||
- [kirkshoop](https://github.com/kirkshoop) made the iterators of the class composable to other libraries.
|
|
||||||
- [wancw](https://github.com/wanwc) fixed a bug that hindered the class to compile with Clang.
|
|
||||||
- Tomas Åblad found a bug in the iterator implementation.
|
|
||||||
|
|
||||||
Thanks a lot for helping out!
|
|
||||||
|
|
||||||
## Execute unit tests with CMake
|
|
||||||
|
|
||||||
To compile and run the tests, you need to execute
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ cmake .
|
|
||||||
$ make
|
|
||||||
$ ctest
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to generate a coverage report with the lcov/genhtml tools, execute this instead:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ cmake .
|
|
||||||
$ make coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note: You need to use GCC for now as otherwise the target coverage doesn't exist!**
|
|
||||||
|
|
||||||
The report is now in the subfolder coverage/index.html
|
|
||||||
|
|
||||||
## Execute unit tests with automake
|
|
||||||
|
|
||||||
To compile the unit tests, you need to execute
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ autoreconf -i
|
|
||||||
$ ./configure
|
|
||||||
$ make
|
|
||||||
```
|
|
||||||
|
|
||||||
The unit tests can then be executed with
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ ./json_unit
|
|
||||||
|
|
||||||
===============================================================================
|
|
||||||
All tests passed (887 assertions in 10 test cases)
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
|
|
|
@ -1,6 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
j = json.load(sys.stdin)
|
|
|
@ -1,8 +0,0 @@
|
||||||
#include "json.h"
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
nlohmann::json j;
|
|
||||||
j << std::cin;
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,11 +1,10 @@
|
||||||
AC_INIT([JSON], [2.0], [mail@nlohmann.me])
|
AC_INIT([JSON], [3.0], [mail@nlohmann.me])
|
||||||
AC_CONFIG_SRCDIR([src/json.h])
|
AC_CONFIG_SRCDIR([src/json.hpp])
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||||
AM_SILENT_RULES([yes])
|
AM_SILENT_RULES([yes])
|
||||||
|
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
AC_PROG_SED
|
|
||||||
|
|
||||||
AC_CONFIG_FILES(Makefile)
|
AC_CONFIG_FILES(Makefile)
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
2331
doc/Doxyfile
2331
doc/Doxyfile
File diff suppressed because it is too large
Load diff
|
@ -1,53 +0,0 @@
|
||||||
# Reference
|
|
||||||
|
|
||||||
## Nomenclature
|
|
||||||
|
|
||||||
We use the term "JSON" when we mean the [JavaScript Object Notation](http://json.org); that is, the file format. When we talk about the class implementing our library, we use "`json`" (typewriter font). Instances of this class are called "`json` values" to differentiate them from "JSON objects"; that is, unordered mappings, hashes, and whatnot.
|
|
||||||
|
|
||||||
## Types and default values
|
|
||||||
|
|
||||||
This table describes how JSON values are mapped to C++ types.
|
|
||||||
|
|
||||||
| JSON type | value_type | C++ type | type alias | default value |
|
|
||||||
| ----------------------- | -------------------------- | ----------------------------- | ---------------------- | --------------
|
|
||||||
| null | `value_type::null` | `nullptr_t` | - | `nullptr` |
|
|
||||||
| string | `value_type::string` | `std::string` | `json::string_t` | `""` |
|
|
||||||
| number (integer) | `value_type::number` | `int` | `json::number_t` | `0` |
|
|
||||||
| number (floating point) | `value_type::number_float` | `double` | `json::number_float_t` | `0.0` |
|
|
||||||
| array | `value_type::array ` | `std::array<json>` | `json::array_t` | `{}` |
|
|
||||||
| object | `value_type::object` | `std::map<std::string, json>` | `json::object_t` | `{}` |
|
|
||||||
|
|
||||||
The second column list entries of an enumeration `value_type` which can be queried by calling `type()` on a `json` value. The column "C++ types" list the internal type that is used to represent the respective JSON value. The "type alias" column furthermore lists type aliases that are used in the `json` class to allow for more flexibility. The last column list the default value; that is, the value that is set if none is passed to the constructor or that is set if `clear()` is called.
|
|
||||||
|
|
||||||
## Type conversions
|
|
||||||
|
|
||||||
There are only a few type conversions possible:
|
|
||||||
|
|
||||||
- An integer number can be translated to a floating point number.
|
|
||||||
- A floating point number can be translated to an integer number. Note the number is truncated and not rounded, ceiled or floored.
|
|
||||||
- Any value (i.e., boolean, string, number, null) but JSON objects can be translated into an array. The result is a singleton array that consists of the value before.
|
|
||||||
- Any other conversion will throw a `std::logic_error` exception.
|
|
||||||
|
|
||||||
When compatible, `json` values **implicitly convert** to `std::string`, `int`, `double`, `json::array_t`, and `json::object_t`. Furthermore, **explicit type conversion** is possible using the `get<>()` function with the aforementioned types.
|
|
||||||
|
|
||||||
## Initialization
|
|
||||||
|
|
||||||
`json` values can be created from many literals and variable types:
|
|
||||||
|
|
||||||
| JSON type | literal/variable types | examples |
|
|
||||||
| --------- | ---------------------- | -------- |
|
|
||||||
| none | null pointer literal, `nullptr_t` type, no value | `nullptr` |
|
|
||||||
| boolean | boolean literals, `bool` type, `json::boolean_t` type | `true`, `false` |
|
|
||||||
| string | string literal, `char*` type, `std::string` type, `std::string&&` rvalue reference, `json::string_t` type | `"Hello"` |
|
|
||||||
| number (integer) | integer literal, `short int` type, `int` type, `json_number_t` type | `42` |
|
|
||||||
| number (floating point) | floating point literal, `float` type, `double` type, `json::number_float_t` type | `3.141529`
|
|
||||||
| array | initializer list whose elements are `json` values (or can be translated into `json` values using the rules above), `std::vector<json>` type, `json::array_t` type, `json::array_t&&` rvalue reference | `{1, 2, 3, true, "foo"}` |
|
|
||||||
| object | initializer list whose elements are pairs of a string literal and a `json` value (or can be translated into `json` values using the rules above), `std::map<std::string, json>` type, `json::object_t` type, `json::object_t&&` rvalue reference | `{ {"key1", 42}, {"key2", false} }` |
|
|
||||||
|
|
||||||
## Number types
|
|
||||||
|
|
||||||
[![JSON number format](http://json.org/number.gif)](http://json.org)
|
|
||||||
|
|
||||||
The JSON specification explicitly does not define an internal number representation, but only the syntax of how numbers can be written down. Consequently, we would need to use the largest possible floating point number format (e.g., `long double`) to internally store JSON numbers.
|
|
||||||
|
|
||||||
However, this would be a waste of space, so we let the JSON parser decide which format to use: If the number can be precisely stored in an `int`, we use an `int` to store it. However, if it is a floating point number, we use `double` to store it.
|
|
3174
header_only/json.h
3174
header_only/json.h
File diff suppressed because it is too large
Load diff
2598
src/json.cc
2598
src/json.cc
File diff suppressed because it is too large
Load diff
576
src/json.h
576
src/json.h
|
@ -1,576 +0,0 @@
|
||||||
/*!
|
|
||||||
@file
|
|
||||||
@copyright The code is licensed under the MIT License
|
|
||||||
<http://opensource.org/licenses/MIT>,
|
|
||||||
Copyright (c) 2013-2015 Niels Lohmann.
|
|
||||||
|
|
||||||
@author Niels Lohmann <http://nlohmann.me>
|
|
||||||
|
|
||||||
@see https://github.com/nlohmann/json
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <initializer_list> // std::initializer_list
|
|
||||||
#include <iostream> // std::istream, std::ostream
|
|
||||||
#include <map> // std::map
|
|
||||||
#include <string> // std::string
|
|
||||||
#include <vector> // std::vector
|
|
||||||
#include <iterator> // std::iterator
|
|
||||||
#include <limits> // std::numeric_limits
|
|
||||||
#include <functional> // std::hash
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief JSON for Modern C++
|
|
||||||
|
|
||||||
The size of a JSON object is 16 bytes: 8 bytes for the value union whose
|
|
||||||
largest item is a pointer type and another 8 byte for an element of the
|
|
||||||
type union. The latter only needs 1 byte - the remaining 7 bytes are wasted
|
|
||||||
due to alignment.
|
|
||||||
|
|
||||||
@see http://stackoverflow.com/questions/7758580/writing-your-own-stl-container/7759622#7759622
|
|
||||||
|
|
||||||
@bug Numbers are currently handled too generously. There are several formats
|
|
||||||
that are forbidden by the standard, but are accepted by the parser.
|
|
||||||
|
|
||||||
@todo Implement json::insert(), json::emplace(), json::emplace_back, json::erase
|
|
||||||
*/
|
|
||||||
class json
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// forward declaration to friend this class
|
|
||||||
class iterator;
|
|
||||||
class const_iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// container types
|
|
||||||
/// the type of elements in a JSON class
|
|
||||||
using value_type = json;
|
|
||||||
/// the type of element references
|
|
||||||
using reference = json&;
|
|
||||||
/// the type of const element references
|
|
||||||
using const_reference = const json&;
|
|
||||||
/// the type of pointers to elements
|
|
||||||
using pointer = json*;
|
|
||||||
/// the type of const pointers to elements
|
|
||||||
using const_pointer = const json*;
|
|
||||||
/// a type to represent differences between iterators
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
/// a type to represent container sizes
|
|
||||||
using size_type = std::size_t;
|
|
||||||
/// an iterator for a JSON container
|
|
||||||
using iterator = json::iterator;
|
|
||||||
/// a const iterator for a JSON container
|
|
||||||
using const_iterator = json::const_iterator;
|
|
||||||
/// a reverse iterator for a JSON container
|
|
||||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
||||||
/// a const reverse iterator for a JSON container
|
|
||||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
||||||
|
|
||||||
/// a type for an object
|
|
||||||
using object_t = std::map<std::string, json>;
|
|
||||||
/// a type for an array
|
|
||||||
using array_t = std::vector<json>;
|
|
||||||
/// a type for a string
|
|
||||||
using string_t = std::string;
|
|
||||||
/// a type for a Boolean
|
|
||||||
using boolean_t = bool;
|
|
||||||
/// a type for an integer number
|
|
||||||
using number_t = int64_t;
|
|
||||||
/// a type for a floating point number
|
|
||||||
using number_float_t = double;
|
|
||||||
/// a type for list initialization
|
|
||||||
using list_init_t = std::initializer_list<json>;
|
|
||||||
|
|
||||||
/// a JSON value
|
|
||||||
union value
|
|
||||||
{
|
|
||||||
/// array as pointer to array_t
|
|
||||||
array_t* array;
|
|
||||||
/// object as pointer to object_t
|
|
||||||
object_t* object;
|
|
||||||
/// string as pointer to string_t
|
|
||||||
string_t* string;
|
|
||||||
/// Boolean
|
|
||||||
boolean_t boolean;
|
|
||||||
/// number (integer)
|
|
||||||
number_t number;
|
|
||||||
/// number (float)
|
|
||||||
number_float_t number_float;
|
|
||||||
|
|
||||||
/// default constructor
|
|
||||||
value() = default;
|
|
||||||
/// constructor for arrays
|
|
||||||
value(array_t*);
|
|
||||||
/// constructor for objects
|
|
||||||
value(object_t*);
|
|
||||||
/// constructor for strings
|
|
||||||
value(string_t*);
|
|
||||||
/// constructor for Booleans
|
|
||||||
value(boolean_t);
|
|
||||||
/// constructor for numbers (integer)
|
|
||||||
value(number_t);
|
|
||||||
/// constructor for numbers (float)
|
|
||||||
value(number_float_t);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// possible types of a JSON object
|
|
||||||
enum class value_t : uint8_t
|
|
||||||
{
|
|
||||||
/// ordered collection of values
|
|
||||||
array = 0,
|
|
||||||
/// unordered set of name/value pairs
|
|
||||||
object,
|
|
||||||
/// null value
|
|
||||||
null,
|
|
||||||
/// string value
|
|
||||||
string,
|
|
||||||
/// Boolean value
|
|
||||||
boolean,
|
|
||||||
/// number value (integer)
|
|
||||||
number,
|
|
||||||
/// number value (float)
|
|
||||||
number_float
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// create an object according to given type
|
|
||||||
json(const value_t);
|
|
||||||
/// create a null object
|
|
||||||
json() noexcept;
|
|
||||||
/// create a null object
|
|
||||||
json(std::nullptr_t) noexcept;
|
|
||||||
/// create a string object from a C++ string
|
|
||||||
json(const std::string&);
|
|
||||||
/// create a string object from a C++ string (move)
|
|
||||||
json(std::string&&);
|
|
||||||
/// create a string object from a C string
|
|
||||||
json(const char*);
|
|
||||||
/// create a Boolean object
|
|
||||||
json(const bool) noexcept;
|
|
||||||
/// create an array
|
|
||||||
json(const array_t&);
|
|
||||||
/// create an array (move)
|
|
||||||
json(array_t&&);
|
|
||||||
/// create an object
|
|
||||||
json(const object_t&);
|
|
||||||
/// create an object (move)
|
|
||||||
json(object_t&&);
|
|
||||||
/// create from an initializer list (to an array or object)
|
|
||||||
json(list_init_t, bool = true, value_t = value_t::array);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief create a number object (integer)
|
|
||||||
@param n an integer number to wrap in a JSON object
|
|
||||||
*/
|
|
||||||
template<typename T, typename
|
|
||||||
std::enable_if<
|
|
||||||
std::numeric_limits<T>::is_integer, T>::type
|
|
||||||
= 0>
|
|
||||||
json(const T n) noexcept
|
|
||||||
: final_type_(0), type_(value_t::number),
|
|
||||||
value_(static_cast<number_t>(n))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief create a number object (float)
|
|
||||||
@param n a floating point number to wrap in a JSON object
|
|
||||||
*/
|
|
||||||
template<typename T, typename = typename
|
|
||||||
std::enable_if<
|
|
||||||
std::is_floating_point<T>::value>::type
|
|
||||||
>
|
|
||||||
json(const T n) noexcept
|
|
||||||
: final_type_(0), type_(value_t::number_float),
|
|
||||||
value_(static_cast<number_float_t>(n))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief create an array object
|
|
||||||
@param v any type of container whose elements can be use to construct
|
|
||||||
JSON objects (e.g., std::vector, std::set, std::array)
|
|
||||||
@note For some reason, we need to explicitly forbid JSON iterator types.
|
|
||||||
*/
|
|
||||||
template <class V, typename
|
|
||||||
std::enable_if<
|
|
||||||
not std::is_same<V, json::iterator>::value and
|
|
||||||
not std::is_same<V, json::const_iterator>::value and
|
|
||||||
not std::is_same<V, json::reverse_iterator>::value and
|
|
||||||
not std::is_same<V, json::const_reverse_iterator>::value and
|
|
||||||
std::is_constructible<json, typename V::value_type>::value, int>::type
|
|
||||||
= 0>
|
|
||||||
json(const V& v) : json(array_t(v.begin(), v.end()))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief create a JSON object
|
|
||||||
@param v any type of associative container whose elements can be use to
|
|
||||||
construct JSON objects (e.g., std::map<std::string, *>)
|
|
||||||
*/
|
|
||||||
template <class V, typename
|
|
||||||
std::enable_if<
|
|
||||||
std::is_constructible<std::string, typename V::key_type>::value and
|
|
||||||
std::is_constructible<json, typename V::mapped_type>::value, int>::type
|
|
||||||
= 0>
|
|
||||||
json(const V& v) : json(object_t(v.begin(), v.end()))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// copy constructor
|
|
||||||
json(const json&);
|
|
||||||
/// move constructor
|
|
||||||
json(json&&) noexcept;
|
|
||||||
|
|
||||||
/// copy assignment
|
|
||||||
json& operator=(json) noexcept;
|
|
||||||
|
|
||||||
/// destructor
|
|
||||||
~json() noexcept;
|
|
||||||
|
|
||||||
/// explicit keyword to force array creation
|
|
||||||
static json array(list_init_t = list_init_t());
|
|
||||||
/// explicit keyword to force object creation
|
|
||||||
static json object(list_init_t = list_init_t());
|
|
||||||
|
|
||||||
/// create from string representation
|
|
||||||
static json parse(const std::string&);
|
|
||||||
/// create from string representation
|
|
||||||
static json parse(const char*);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// return the type as string
|
|
||||||
std::string type_name() const noexcept;
|
|
||||||
|
|
||||||
/// dump the object (with pretty printer)
|
|
||||||
std::string dump(const bool, const unsigned int, unsigned int = 0) const noexcept;
|
|
||||||
/// replaced a character in a string with another string
|
|
||||||
void replaceChar(std::string& str, char c, const std::string& replacement) const;
|
|
||||||
/// escapes special characters to safely dump the string
|
|
||||||
std::string escapeString(const std::string&) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// explicit value conversion
|
|
||||||
template<typename T>
|
|
||||||
T get() const;
|
|
||||||
|
|
||||||
/// implicit conversion to string representation
|
|
||||||
operator std::string() const;
|
|
||||||
/// implicit conversion to integer (only for numbers)
|
|
||||||
operator int() const;
|
|
||||||
/// implicit conversion to integer (only for numbers)
|
|
||||||
operator int64_t() const;
|
|
||||||
/// implicit conversion to double (only for numbers)
|
|
||||||
operator double() const;
|
|
||||||
/// implicit conversion to Boolean (only for Booleans)
|
|
||||||
operator bool() const;
|
|
||||||
/// implicit conversion to JSON vector (not for objects)
|
|
||||||
operator array_t() const;
|
|
||||||
/// implicit conversion to JSON map (only for objects)
|
|
||||||
operator object_t() const;
|
|
||||||
|
|
||||||
/// serialize to stream
|
|
||||||
friend std::ostream& operator<<(std::ostream& o, const json& j)
|
|
||||||
{
|
|
||||||
o << j.dump();
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
/// serialize to stream
|
|
||||||
friend std::ostream& operator>>(const json& j, std::ostream& o)
|
|
||||||
{
|
|
||||||
o << j.dump();
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// deserialize from stream
|
|
||||||
friend std::istream& operator>>(std::istream& i, json& j)
|
|
||||||
{
|
|
||||||
j = parser(i).parse();
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
/// deserialize from stream
|
|
||||||
friend std::istream& operator<<(json& j, std::istream& i)
|
|
||||||
{
|
|
||||||
j = parser(i).parse();
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// explicit serialization
|
|
||||||
std::string dump(int = -1) const noexcept;
|
|
||||||
|
|
||||||
/// add constructible objects to an array
|
|
||||||
template<class T, typename std::enable_if<std::is_constructible<json, T>::value>::type = 0>
|
|
||||||
json & operator+=(const T& o)
|
|
||||||
{
|
|
||||||
push_back(json(o));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// add an object/array to an array
|
|
||||||
json& operator+=(const json&);
|
|
||||||
|
|
||||||
/// add a pair to an object
|
|
||||||
json& operator+=(const object_t::value_type&);
|
|
||||||
/// add a list of elements to array or list of pairs to object
|
|
||||||
json& operator+=(list_init_t);
|
|
||||||
|
|
||||||
/// add constructible objects to an array
|
|
||||||
template<class T, typename std::enable_if<std::is_constructible<json, T>::value>::type = 0>
|
|
||||||
void push_back(const T& o)
|
|
||||||
{
|
|
||||||
push_back(json(o));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// add an object/array to an array
|
|
||||||
void push_back(const json&);
|
|
||||||
/// add an object/array to an array (move)
|
|
||||||
void push_back(json&&);
|
|
||||||
|
|
||||||
/// add a pair to an object
|
|
||||||
void push_back(const object_t::value_type&);
|
|
||||||
/// add a list of elements to array or list of pairs to object
|
|
||||||
void push_back(list_init_t);
|
|
||||||
|
|
||||||
/// operator to set an element in an array
|
|
||||||
reference operator[](const int);
|
|
||||||
/// operator to get an element in an array
|
|
||||||
const_reference operator[](const int) const;
|
|
||||||
/// operator to get an element in an array
|
|
||||||
reference at(const int);
|
|
||||||
/// operator to get an element in an array
|
|
||||||
const_reference at(const int) const;
|
|
||||||
|
|
||||||
/// operator to set an element in an object
|
|
||||||
reference operator[](const std::string&);
|
|
||||||
/// operator to set an element in an object
|
|
||||||
reference operator[](const char*);
|
|
||||||
/// operator to get an element in an object
|
|
||||||
const_reference operator[](const std::string&) const;
|
|
||||||
/// operator to get an element in an object
|
|
||||||
const_reference operator[](const char*) const;
|
|
||||||
/// operator to set an element in an object
|
|
||||||
reference at(const std::string&);
|
|
||||||
/// operator to set an element in an object
|
|
||||||
reference at(const char*);
|
|
||||||
/// operator to get an element in an object
|
|
||||||
const_reference at(const std::string&) const;
|
|
||||||
/// operator to get an element in an object
|
|
||||||
const_reference at(const char*) const;
|
|
||||||
|
|
||||||
/// return the number of stored values
|
|
||||||
size_type size() const noexcept;
|
|
||||||
/// return the maximal number of values that can be stored
|
|
||||||
size_type max_size() const noexcept;
|
|
||||||
/// checks whether object is empty
|
|
||||||
bool empty() const noexcept;
|
|
||||||
/// removes all elements from compounds and resets values to default
|
|
||||||
void clear() noexcept;
|
|
||||||
|
|
||||||
/// swaps content with other object
|
|
||||||
void swap(json&) noexcept;
|
|
||||||
|
|
||||||
/// return the type of the object
|
|
||||||
value_t type() const noexcept;
|
|
||||||
|
|
||||||
/// find an element in an object (returns end() iterator otherwise)
|
|
||||||
iterator find(const std::string&);
|
|
||||||
/// find an element in an object (returns end() iterator otherwise)
|
|
||||||
const_iterator find(const std::string&) const;
|
|
||||||
/// find an element in an object (returns end() iterator otherwise)
|
|
||||||
iterator find(const char*);
|
|
||||||
/// find an element in an object (returns end() iterator otherwise)
|
|
||||||
const_iterator find(const char*) const;
|
|
||||||
|
|
||||||
/// lexicographically compares the values
|
|
||||||
bool operator==(const json&) const noexcept;
|
|
||||||
/// lexicographically compares the values
|
|
||||||
bool operator!=(const json&) const noexcept;
|
|
||||||
|
|
||||||
/// returns an iterator to the beginning (array/object)
|
|
||||||
iterator begin() noexcept;
|
|
||||||
/// returns an iterator to the end (array/object)
|
|
||||||
iterator end() noexcept;
|
|
||||||
/// returns an iterator to the beginning (array/object)
|
|
||||||
const_iterator begin() const noexcept;
|
|
||||||
/// returns an iterator to the end (array/object)
|
|
||||||
const_iterator end() const noexcept;
|
|
||||||
/// returns an iterator to the beginning (array/object)
|
|
||||||
const_iterator cbegin() const noexcept;
|
|
||||||
/// returns an iterator to the end (array/object)
|
|
||||||
const_iterator cend() const noexcept;
|
|
||||||
/// returns a reverse iterator to the beginning
|
|
||||||
reverse_iterator rbegin() noexcept;
|
|
||||||
/// returns a reverse iterator to the end
|
|
||||||
reverse_iterator rend() noexcept;
|
|
||||||
/// returns a reverse iterator to the beginning
|
|
||||||
const_reverse_iterator crbegin() const noexcept;
|
|
||||||
/// returns a reverse iterator to the end
|
|
||||||
const_reverse_iterator crend() const noexcept;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// whether the type is final
|
|
||||||
unsigned final_type_ : 1;
|
|
||||||
/// the type of this object
|
|
||||||
value_t type_ = value_t::null;
|
|
||||||
/// the payload
|
|
||||||
value value_ {};
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// an iterator
|
|
||||||
class iterator : public std::iterator<std::bidirectional_iterator_tag, json>
|
|
||||||
{
|
|
||||||
friend class json;
|
|
||||||
friend class json::const_iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
iterator() = default;
|
|
||||||
iterator(json*, bool);
|
|
||||||
iterator(const iterator&);
|
|
||||||
~iterator();
|
|
||||||
|
|
||||||
iterator& operator=(iterator);
|
|
||||||
bool operator==(const iterator&) const;
|
|
||||||
bool operator!=(const iterator&) const;
|
|
||||||
iterator& operator++();
|
|
||||||
iterator& operator--();
|
|
||||||
json& operator*() const;
|
|
||||||
json* operator->() const;
|
|
||||||
|
|
||||||
/// getter for the key (in case of objects)
|
|
||||||
std::string key() const;
|
|
||||||
/// getter for the value
|
|
||||||
json& value() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// a JSON value
|
|
||||||
json* object_ = nullptr;
|
|
||||||
/// an iterator for JSON arrays
|
|
||||||
array_t::iterator* vi_ = nullptr;
|
|
||||||
/// an iterator for JSON objects
|
|
||||||
object_t::iterator* oi_ = nullptr;
|
|
||||||
/// whether iterator points to a valid object
|
|
||||||
bool invalid = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// a const iterator
|
|
||||||
class const_iterator : public std::iterator<std::bidirectional_iterator_tag, const json>
|
|
||||||
{
|
|
||||||
friend class json;
|
|
||||||
|
|
||||||
public:
|
|
||||||
const_iterator() = default;
|
|
||||||
const_iterator(const json*, bool);
|
|
||||||
const_iterator(const const_iterator&);
|
|
||||||
const_iterator(const json::iterator&);
|
|
||||||
~const_iterator();
|
|
||||||
|
|
||||||
const_iterator& operator=(const_iterator);
|
|
||||||
bool operator==(const const_iterator&) const;
|
|
||||||
bool operator!=(const const_iterator&) const;
|
|
||||||
const_iterator& operator++();
|
|
||||||
const_iterator& operator--();
|
|
||||||
const json& operator*() const;
|
|
||||||
const json* operator->() const;
|
|
||||||
|
|
||||||
/// getter for the key (in case of objects)
|
|
||||||
std::string key() const;
|
|
||||||
/// getter for the value
|
|
||||||
const json& value() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// a JSON value
|
|
||||||
const json* object_ = nullptr;
|
|
||||||
/// an iterator for JSON arrays
|
|
||||||
array_t::const_iterator* vi_ = nullptr;
|
|
||||||
/// an iterator for JSON objects
|
|
||||||
object_t::const_iterator* oi_ = nullptr;
|
|
||||||
/// whether iterator reached past the end
|
|
||||||
bool invalid = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// a helper class to parse a JSON object
|
|
||||||
class parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// a parser reading from a C string
|
|
||||||
parser(const char*);
|
|
||||||
/// a parser reading from a C++ string
|
|
||||||
parser(const std::string&);
|
|
||||||
/// a parser reading from an input stream
|
|
||||||
parser(std::istream&);
|
|
||||||
/// destructor of the parser
|
|
||||||
~parser() = default;
|
|
||||||
|
|
||||||
// no copy constructor
|
|
||||||
parser(const parser&) = delete;
|
|
||||||
// no copy assignment
|
|
||||||
parser& operator=(parser) = delete;
|
|
||||||
|
|
||||||
/// parse and return a JSON object
|
|
||||||
json parse();
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// read the next character, stripping whitespace
|
|
||||||
bool next();
|
|
||||||
/// raise an exception with an error message
|
|
||||||
[[noreturn]] inline void error(const std::string&) const;
|
|
||||||
/// parse a quoted string
|
|
||||||
inline std::string parseString();
|
|
||||||
/// transforms a unicode codepoint to it's UTF-8 presentation
|
|
||||||
std::string codePointToUTF8(unsigned int codePoint) const;
|
|
||||||
/// parses 4 hex characters that represent a unicode code point
|
|
||||||
inline unsigned int parse4HexCodePoint();
|
|
||||||
/// parses \uXXXX[\uXXXX] unicode escape characters
|
|
||||||
inline std::string parseUnicodeEscape();
|
|
||||||
/// parse a Boolean "true"
|
|
||||||
inline void parseTrue();
|
|
||||||
/// parse a Boolean "false"
|
|
||||||
inline void parseFalse();
|
|
||||||
/// parse a null object
|
|
||||||
inline void parseNull();
|
|
||||||
/// a helper function to expect a certain character
|
|
||||||
inline void expect(const char);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// a buffer of the input
|
|
||||||
std::string buffer_ {};
|
|
||||||
/// the current character
|
|
||||||
char current_ {};
|
|
||||||
/// the position inside the input buffer
|
|
||||||
std::size_t pos_ = 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// user-defined literal operator to create JSON objects from strings
|
|
||||||
nlohmann::json operator "" _json(const char*, std::size_t);
|
|
||||||
|
|
||||||
// specialization of std::swap, and std::hash
|
|
||||||
namespace std
|
|
||||||
{
|
|
||||||
template <>
|
|
||||||
/// swaps the values of two JSON objects
|
|
||||||
inline void swap(nlohmann::json& j1,
|
|
||||||
nlohmann::json& j2) noexcept(
|
|
||||||
is_nothrow_move_constructible<nlohmann::json>::value and
|
|
||||||
is_nothrow_move_assignable<nlohmann::json>::value
|
|
||||||
)
|
|
||||||
{
|
|
||||||
j1.swap(j2);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
/// hash value for JSON objects
|
|
||||||
struct hash<nlohmann::json>
|
|
||||||
{
|
|
||||||
size_t operator()(const nlohmann::json& j) const
|
|
||||||
{
|
|
||||||
// a naive hashing via the string representation
|
|
||||||
return hash<std::string>()(j.dump());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
2285
src/json.hpp
Normal file
2285
src/json.hpp
Normal file
File diff suppressed because it is too large
Load diff
2258
test/json_unit.cc
2258
test/json_unit.cc
File diff suppressed because it is too large
Load diff
2413
test/unit.cpp
Normal file
2413
test/unit.cpp
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue