Commit 3586767c by Niels

Merge branch 'release/v2.0.2'

parents 8b395700 a05df038
......@@ -12,3 +12,5 @@ me.nlohmann.json.docset
android
doc/xml
benchmarks/files/numbers/*.json
......@@ -168,3 +168,9 @@ script:
- if [ `which valgrind` ]; then
valgrind --error-exitcode=1 --leak-check=full ./json_unit ;
fi
- if [ `which brew` ]; then
brew update ;
brew tap nlohmann/json ;
brew install nlohmann_json --HEAD ;
brew test nlohmann_json ;
fi
cmake_minimum_required(VERSION 3.0)
# define the project
project(nlohmann_json VERSION 2.0.0 LANGUAGES CXX)
project(nlohmann_json VERSION 2.0.2 LANGUAGES CXX)
enable_testing()
......
# Change Log
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased](https://github.com/nlohmann/json/tree/HEAD)
## [v2.0.2](https://github.com/nlohmann/json/releases/tag/v2.0.2) (2016-07-30)
[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...v2.0.2)
[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.0...HEAD)
- value\(\) does not work with \_json\_pointer types [\#283](https://github.com/nlohmann/json/issues/283)
- Easy serialization of classes [\#280](https://github.com/nlohmann/json/issues/280)
- Build error for std::int64 [\#282](https://github.com/nlohmann/json/issues/282)
- hexify\(\) function emits conversion warning [\#270](https://github.com/nlohmann/json/issues/270)
- let the makefile choose the correct sed [\#279](https://github.com/nlohmann/json/pull/279) ([murinicanor](https://github.com/murinicanor))
- Update hexify to use array lookup instead of ternary \(\#270\) [\#275](https://github.com/nlohmann/json/pull/275) ([dtoma](https://github.com/dtoma))
## [v2.0.1](https://github.com/nlohmann/json/releases/tag/v2.0.1) (2016-06-28)
[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.0...v2.0.1)
- dump\(\) performance degradation in v2 [\#272](https://github.com/nlohmann/json/issues/272)
......
......@@ -10,6 +10,7 @@ all: json_unit
# clean up
clean:
rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM
rm -fr benchmarks/files/numbers/*.json
$(MAKE) clean -Cdoc
......@@ -85,6 +86,7 @@ pretty:
# benchmarks
json_benchmarks: benchmarks/benchmarks.cpp benchmarks/benchpress.hpp benchmarks/cxxopts.hpp src/json.hpp
cd benchmarks/files/numbers ; python generate.py
$(CXX) -std=c++11 $(CXXFLAGS) -O3 -flto -I src -I benchmarks $< $(LDFLAGS) -o $@
./json_benchmarks
......@@ -93,7 +95,9 @@ json_benchmarks: benchmarks/benchmarks.cpp benchmarks/benchpress.hpp benchmarks/
# changelog
##########################################################################
NEXT_VERSION ?= "unreleased"
ChangeLog.md:
github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s
github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION)
gsed -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md
gsed -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md
......@@ -4,7 +4,7 @@
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/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)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/wuiuqYiYqRTdI3rG)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN)
[![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 Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
......@@ -24,7 +24,7 @@ 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). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs.
- **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).
- **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) may be even faster (but would consist of more files which makes the integration harder).
See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information.
......@@ -344,8 +344,8 @@ json j_result = j_original.patch(j_patch);
json::diff(j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op":"remove","path":"/hello" },
// { "op":"add","path":"/foo","value":"bar" }
// { "op": "remove","path": "/hello" },
// { "op": "add", "path": "/foo", "value": "bar" }
// ]
```
......@@ -485,6 +485,7 @@ I deeply appreciate the help of the following people.
- [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal).
- [Mário Feroldi](https://github.com/thelostt) fixed a small typo.
- [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
- [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
Thanks a lot for helping out!
......@@ -504,7 +505,7 @@ $ make
$ ./json_unit "*"
===============================================================================
All tests passed (5568718 assertions in 32 test cases)
All tests passed (8905012 assertions in 32 test cases)
```
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
......@@ -44,6 +44,36 @@ BENCHMARK("parse twitter.json", [](benchpress::context* ctx)
}
})
BENCHMARK("parse numbers/floats.json", [](benchpress::context* ctx)
{
for (size_t i = 0; i < ctx->num_iterations(); ++i)
{
std::ifstream input_file("benchmarks/files/numbers/floats.json");
nlohmann::json j;
j << input_file;
}
})
BENCHMARK("parse numbers/signed_ints.json", [](benchpress::context* ctx)
{
for (size_t i = 0; i < ctx->num_iterations(); ++i)
{
std::ifstream input_file("benchmarks/files/numbers/signed_ints.json");
nlohmann::json j;
j << input_file;
}
})
BENCHMARK("parse numbers/unsigned_ints.json", [](benchpress::context* ctx)
{
for (size_t i = 0; i < ctx->num_iterations(); ++i)
{
std::ifstream input_file("benchmarks/files/numbers/unsigned_ints.json");
nlohmann::json j;
j << input_file;
}
})
BENCHMARK("dump jeopardy.json", [](benchpress::context* ctx)
{
std::ifstream input_file("benchmarks/files/jeopardy/jeopardy.json");
......
#!/usr/bin/env python
import json
import random
import sys
random.seed(0)
# floats
result_floats = []
for x in range(0, 1000000):
result_floats.append(random.uniform(-100000000.0, 100000000.0))
json.dump(result_floats, open("floats.json", "w"), indent=2)
# unsigned integers
result_uints = []
for x in range(0, 1000000):
result_uints.append(random.randint(0, 18446744073709551615))
json.dump(result_uints, open("unsigned_ints.json", "w"), indent=2)
# signed integers
result_sints = []
for x in range(0, 1000000):
result_sints.append(random.randint(-9223372036854775808, 9223372036854775807))
json.dump(result_sints, open("signed_ints.json", "w"), indent=2)
......@@ -5,7 +5,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "JSON for Modern C++"
PROJECT_NUMBER = 2.0.0
PROJECT_NUMBER = 2.0.2
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY = .
......
SRCDIR = ../src
SED:=$(shell command -v gsed || which sed)
all: doxygen
......@@ -52,10 +53,10 @@ clean:
# create Doxygen documentation
doxygen: create_output create_links
doxygen
gsed -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType &gt;@@g' html/*.html
gsed -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberFloatType,&#160;AllocatorType&#160;&gt;@@g' html/*.html
gsed -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType &gt;@@g' html/*.html
gsed -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
$(SED) -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType &gt;@@g' html/*.html
$(SED) -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberFloatType,&#160;AllocatorType&#160;&gt;@@g' html/*.html
$(SED) -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType &gt;@@g' html/*.html
$(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
upload: clean doxygen check_output
cd html ; ../scripts/git-update-ghpages nlohmann/json
......@@ -70,14 +71,14 @@ upload: clean doxygen check_output
# create docset for Dash
docset: create_output
cp Doxyfile Doxyfile_docset
gsed -i 's/DISABLE_INDEX = NO/DISABLE_INDEX = YES/' Doxyfile_docset
gsed -i 's/SEARCHENGINE = YES/SEARCHENGINE = NO/' Doxyfile_docset
gsed -i 's@HTML_EXTRA_STYLESHEET = css/mylayout.css@HTML_EXTRA_STYLESHEET = css/mylayout_docset.css@' Doxyfile_docset
$(SED) -i 's/DISABLE_INDEX = NO/DISABLE_INDEX = YES/' Doxyfile_docset
$(SED) -i 's/SEARCHENGINE = YES/SEARCHENGINE = NO/' Doxyfile_docset
$(SED) -i 's@HTML_EXTRA_STYLESHEET = css/mylayout.css@HTML_EXTRA_STYLESHEET = css/mylayout_docset.css@' Doxyfile_docset
rm -fr html *.docset
doxygen Doxyfile_docset
gsed -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType &gt;@@g' html/*.html
gsed -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberFloatType,&#160;AllocatorType&#160;&gt;@@g' html/*.html
$(SED) -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType &gt;@@g' html/*.html
$(SED) -i 's@&lt;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberFloatType,&#160;AllocatorType&#160;&gt;@@g' html/*.html
make -C html
mv html/*.docset .
gsed -i 's@<string>doxygen</string>@<string>json</string>@' me.nlohmann.json.docset/Contents/Info.plist
$(SED) -i 's@<string>doxygen</string>@<string>json</string>@' me.nlohmann.json.docset/Contents/Info.plist
rm -fr Doxyfile_docset html
<a target="_blank" href="http://melpon.org/wandbox/permlink/k5KRwVJ42VggxxzX"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN"><b>online</b></a>
\ No newline at end of file
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// create a JSON object with different entry types
json j =
{
{"integer", 1},
{"floating", 42.23},
{"string", "hello world"},
{"boolean", true},
{"object", {{"key1", 1}, {"key2", 2}}},
{"array", {1, 2, 3}}
};
// access existing values
int v_integer = j.value("/integer"_json_pointer, 0);
double v_floating = j.value("/floating"_json_pointer, 47.11);
// access nonexisting values and rely on default value
std::string v_string = j.value("/nonexisting"_json_pointer, "oops");
bool v_boolean = j.value("/nonexisting"_json_pointer, false);
// output values
std::cout << std::boolalpha << v_integer << " " << v_floating
<< " " << v_string << " " << v_boolean << "\n";
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/K4L4D6nibuGXbjfd"><b>online</b></a>
\ No newline at end of file
......@@ -12,7 +12,6 @@ int main()
auto count_three = j_object.count("three");
// print values
std::cout << std::boolalpha;
std::cout << "number of elements with key \"two\": " << count_two << '\n';
std::cout << "number of elements with key \"three\": " << count_three << '\n';
}
<a target="_blank" href="http://melpon.org/wandbox/permlink/XoXqF9LlWu8jlNgE"><b>online</b></a>
\ No newline at end of file
<a target="_blank" href="http://melpon.org/wandbox/permlink/yv5TMrq9qREivvHf"><b>online</b></a>
\ No newline at end of file
......@@ -268,4 +268,4 @@ The container functions known from STL have been extended to support the differe
@author [Niels Lohmann](http://nlohmann.me)
@see https://github.com/nlohmann/json to download the source code
@version 2.0.0
@version 2.0.2
doc/json.gif

441 KB | W: | H:

doc/json.gif

440 KB | W: | H:

doc/json.gif
doc/json.gif
doc/json.gif
doc/json.gif
  • 2-up
  • Swipe
  • Onion skin
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
| | |__ | | | | | | version 2.0.0
| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
......@@ -32,11 +32,10 @@ SOFTWARE.
#include <algorithm>
#include <array>
#include <cassert>
#include <cerrno>
#include <ciso646>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <initializer_list>
......@@ -44,8 +43,10 @@ SOFTWARE.
#include <iostream>
#include <iterator>
#include <limits>
#include <locale>
#include <map>
#include <memory>
#include <numeric>
#include <sstream>
#include <stdexcept>
#include <string>
......@@ -53,6 +54,19 @@ SOFTWARE.
#include <utility>
#include <vector>
// exclude unsupported compilers
#if defined(__clang__)
#define CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
#if CLANG_VERSION < 30400
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
#endif
#elif defined(__GNUC__)
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if GCC_VERSION < 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
......@@ -76,7 +90,13 @@ namespace
{
/*!
@brief Helper to determine whether there's a key_type for T.
Thus 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
*/
template<typename T>
struct has_mapped_type
......@@ -90,11 +110,18 @@ struct has_mapped_type
/*!
@brief helper class to create locales with decimal point
This struct is used a default locale during the JSON serialization. JSON
requires the decimal point to be `.`, so this function overloads the
`do_decimal_point()` function to return `.`. This function is called by
float-to-string conversions to retrieve the decimal separator between integer
and fractional parts.
@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315
@since version 2.0.0
*/
class DecimalSeparator : public std::numpunct<char>
struct DecimalSeparator : std::numpunct<char>
{
protected:
char do_decimal_point() const
{
return '.';
......@@ -163,6 +190,13 @@ default)
JSON values can be used like STL containers and provide reverse iterator
access.
@invariant The member variables @a m_value and @a m_type have the following
relationship:
- If `m_type == value_t::object`, then `m_value.object != nullptr`.
- If `m_type == value_t::array`, then `m_value.array != nullptr`.
- If `m_type == value_t::string`, then `m_value.string != nullptr`.
The invariants are checked by member function assert_invariant().
@internal
@note ObjectType trick from http://stackoverflow.com/a/9860911
@endinternal
......@@ -188,13 +222,8 @@ class basic_json
{
private:
/// workaround type for MSVC
using basic_json_t = basic_json<ObjectType,
ArrayType,
StringType,
BooleanType,
NumberIntegerType,
NumberUnsignedType,
NumberFloatType,
using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
AllocatorType>;
public:
......@@ -207,6 +236,8 @@ class basic_json
/////////////////////
/// @name container types
/// The canonic container types to use @ref basic_json like any other STL
/// container.
/// @{
/// the type of elements in a basic_json container
......@@ -256,6 +287,8 @@ class basic_json
///////////////////////////
/// @name JSON value data types
/// The data types to store a JSON value. These types are derived from
/// the template arguments passed to class @ref basic_json.
/// @{
/*!
......@@ -358,7 +391,7 @@ class basic_json
@tparam ArrayType container type to store arrays (e.g., `std::vector` or
`std::list`)
@tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
@tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)
#### Default type
......@@ -595,15 +628,14 @@ class basic_json
> that implementations will agree exactly on their numeric values.
As this range is a subrange (when considered in conjunction with the
number_integer_t type) of the exactly supported range [0, UINT64_MAX], this
class's integer type is interoperable.
number_integer_t type) of the exactly supported range [0, UINT64_MAX],
this class's integer type is interoperable.
#### Storage
Integer number values are stored directly inside a @ref basic_json type.
@sa @ref number_float_t -- type for number values (floating-point)
@sa @ref number_integer_t -- type for number values (integer)
@since version 2.0.0
......@@ -691,7 +723,19 @@ class basic_json
This enumeration collects the different JSON types. It is internally used
to distinguish the stored values, and the functions @ref is_null(), @ref
is_object(), @ref is_array(), @ref is_string(), @ref is_boolean(), @ref
is_number(), and @ref is_discarded() rely on it.
is_number() (with @ref is_number_integer(), @ref is_number_unsigned(), and
@ref is_number_float()), @ref is_discarded(), @ref is_primitive(), and
@ref 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 number_unsigned_t is used for unsigned
integers, @ref number_integer_t is used for signed integers, and @ref
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(const value_t value_type) -- create a JSON value with
the default value for a given type
@since version 1.0.0
*/
......@@ -702,7 +746,7 @@ class basic_json
array, ///< array (ordered collection of values)
string, ///< string value
boolean, ///< boolean value
number_integer, ///< number value (integer)
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
......@@ -722,6 +766,7 @@ class basic_json
};
std::unique_ptr<T, decltype(deleter)> object(alloc.allocate(1), deleter);
alloc.construct(object.get(), std::forward<Args>(args)...);
assert(object.get() != nullptr);
return object.release();
}
......@@ -732,7 +777,24 @@ class basic_json
/*!
@brief a JSON value
The actual storage for a JSON value of the @ref basic_json class.
The actual storage for a JSON value of the @ref basic_json class. This
union combines the different storage types for the JSON value types
defined in @ref value_t.
JSON type | value_t type | used type
--------- | --------------- | ------------------------
object | object | pointer to @ref object_t
array | array | pointer to @ref array_t
string | string | pointer to @ref string_t
boolean | boolean | @ref boolean_t
number | number_integer | @ref number_integer_t
number | number_unsigned | @ref number_unsigned_t
number | number_float | @ref number_float_t
null | null | *no value is stored*
@note Variable-length types (objects, arrays, and strings) are stored as
pointers. The size of the union should not exceed 64 bits if the default
value types are used.
@since version 1.0.0
*/
......@@ -836,6 +898,21 @@ class basic_json
}
};
/*!
@brief checks the class invariants
This function asserts the class invariants. It needs to be called at the
end of every constructor to make sure that created objects respect the
invariant. Furthermore, it has to be called each time the type of a JSON
value is changed, because the invariant expresses a relationship between
@a m_type and @a m_value.
*/
void assert_invariant() const
{
assert(m_type != value_t::object or m_value.object != nullptr);
assert(m_type != value_t::array or m_value.array != nullptr);
assert(m_type != value_t::string or m_value.string != nullptr);
}
public:
//////////////////////////
......@@ -848,6 +925,8 @@ class basic_json
This enumeration lists the parser events that can trigger calling a
callback function of type @ref parser_callback_t during parsing.
@image html callback_events.png "Example when certain parse events are triggered"
@since version 1.0.0
*/
enum class parse_event_t : uint8_t
......@@ -870,12 +949,13 @@ class basic_json
@brief per-element parser callback type
With a parser callback function, the result of parsing a JSON text can be
influenced. When passed to @ref parse(std::istream&, parser_callback_t) or
@ref parse(const string_t&, parser_callback_t), it is called on certain
events (passed as @ref parse_event_t via parameter @a event) with a set
recursion depth @a depth and context JSON value @a parsed. The return
value of the callback function is a boolean indicating whether the element
that emitted the callback shall be kept or not.
influenced. When passed to @ref parse(std::istream&, const
parser_callback_t) or @ref parse(const string_t&, const parser_callback_t),
it is called on certain events (passed as @ref parse_event_t via parameter
@a event) with a set recursion depth @a depth and context JSON value
@a parsed. The return value of the callback function is a boolean
indicating whether the element that emitted the callback shall be kept or
not.
We distinguish six scenarios (determined by the event type) in which the
callback function can be called. The following table describes the values
......@@ -890,6 +970,8 @@ class basic_json
parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array
parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value
@image html callback_events.png "Example when certain parse events are triggered"
Discarding a value (i.e., returning `false`) has different effects
depending on the context in which function was called:
......@@ -915,7 +997,9 @@ class basic_json
@since version 1.0.0
*/
using parser_callback_t = std::function<bool(int depth, parse_event_t event, basic_json& parsed)>;
using parser_callback_t = std::function<bool(int depth,
parse_event_t event,
basic_json& parsed)>;
//////////////////
......@@ -923,6 +1007,8 @@ class basic_json
//////////////////
/// @name constructors and destructors
/// Constructors of class @ref basic_json, copy/move constructor, copy
/// assignment, static functions creating objects, and the destructor.
/// @{
/*!
......@@ -966,7 +1052,9 @@ class basic_json
*/
basic_json(const value_t value_type)
: m_type(value_type), m_value(value_type)
{}
{
assert_invariant();
}
/*!
@brief create a null object (implicitly)
......@@ -974,6 +1062,9 @@ class basic_json
Create a `null` JSON value. This is the implicit version of the `null`
value constructor as it takes no parameters.
@note The class invariant is satisfied, because it poses no requirements
for null values.
@complexity Constant.
@exceptionsafety No-throw guarantee: this constructor never throws
......@@ -1018,7 +1109,9 @@ class basic_json
*/
basic_json(std::nullptr_t) noexcept
: basic_json(value_t::null)
{}
{
assert_invariant();
}
/*!
@brief create an object (explicit)
......@@ -1041,7 +1134,9 @@ class basic_json
*/
basic_json(const object_t& val)
: m_type(value_t::object), m_value(val)
{}
{
assert_invariant();
}
/*!
@brief create an object (implicit)
......@@ -1080,6 +1175,7 @@ class basic_json
using std::begin;
using std::end;
m_value.object = create<object_t>(begin(val), end(val));
assert_invariant();
}
/*!
......@@ -1103,7 +1199,9 @@ class basic_json
*/
basic_json(const array_t& val)
: m_type(value_t::array), m_value(val)
{}
{
assert_invariant();
}
/*!
@brief create an array (implicit)
......@@ -1147,6 +1245,7 @@ class basic_json
using std::begin;
using std::end;
m_value.array = create<array_t>(begin(val), end(val));
assert_invariant();
}
/*!
......@@ -1172,7 +1271,9 @@ class basic_json
*/
basic_json(const string_t& val)
: m_type(value_t::string), m_value(val)
{}
{
assert_invariant();
}
/*!
@brief create a string (explicit)
......@@ -1196,7 +1297,9 @@ class basic_json
*/
basic_json(const typename string_t::value_type* val)
: basic_json(string_t(val))
{}
{
assert_invariant();
}
/*!
@brief create a string (implicit)
......@@ -1227,7 +1330,9 @@ class basic_json
= 0>
basic_json(const CompatibleStringType& val)
: basic_json(string_t(val))
{}
{
assert_invariant();
}
/*!
@brief create a boolean (explicit)
......@@ -1245,7 +1350,9 @@ class basic_json
*/
basic_json(boolean_t val) noexcept
: m_type(value_t::boolean), m_value(val)
{}
{
assert_invariant();
}
/*!
@brief create an integer number (explicit)
......@@ -1278,7 +1385,9 @@ class basic_json
= 0>
basic_json(const number_integer_t val) noexcept
: m_type(value_t::number_integer), m_value(val)
{}
{
assert_invariant();
}
/*!
@brief create an integer number from an enum type (explicit)
......@@ -1308,7 +1417,9 @@ class basic_json
basic_json(const int val) noexcept
: m_type(value_t::number_integer),
m_value(static_cast<number_integer_t>(val))
{}
{
assert_invariant();
}
/*!
@brief create an integer number (implicit)
......@@ -1345,15 +1456,17 @@ class basic_json
basic_json(const CompatibleNumberIntegerType val) noexcept
: m_type(value_t::number_integer),
m_value(static_cast<number_integer_t>(val))
{}
{
assert_invariant();
}
/*!
@brief create an unsigned integer number (explicit)
Create an unsigned integer number JSON value with a given content.
@tparam T helper type to compare number_unsigned_t and unsigned int
(not visible in) the interface.
@tparam T helper type to compare number_unsigned_t and unsigned int (not
visible in) the interface.
@param[in] val an integer to create a JSON number from
......@@ -1372,7 +1485,9 @@ class basic_json
= 0>
basic_json(const number_unsigned_t val) noexcept
: m_type(value_t::number_unsigned), m_value(val)
{}
{
assert_invariant();
}
/*!
@brief create an unsigned number (implicit)
......@@ -1404,7 +1519,9 @@ class basic_json
basic_json(const CompatibleNumberUnsignedType val) noexcept
: m_type(value_t::number_unsigned),
m_value(static_cast<number_unsigned_t>(val))
{}
{
assert_invariant();
}
/*!
@brief create a floating-point number (explicit)
......@@ -1417,8 +1534,8 @@ class basic_json
disallows NaN values:
> Numeric values that cannot be represented in the grammar below (such as
> Infinity and NaN) are not permitted.
In case the parameter @a val is not a number, a JSON null value is
created instead.
In case the parameter @a val is not a number, a JSON null value is created
instead.
@complexity Constant.
......@@ -1439,6 +1556,8 @@ class basic_json
m_type = value_t::null;
m_value = json_value();
}
assert_invariant();
}
/*!
......@@ -1479,7 +1598,9 @@ class basic_json
>
basic_json(const CompatibleNumberFloatType val) noexcept
: basic_json(number_float_t(val))
{}
{
assert_invariant();
}
/*!
@brief create a container (array or object) from an initializer list
......@@ -1491,21 +1612,21 @@ class basic_json
1. If the list is empty, an empty JSON object value `{}` is created.
2. If the list consists of pairs whose first element is a string, a JSON
object value is created where the first elements of the pairs are treated
as keys and the second elements are as values.
object value is created where the first elements of the pairs are
treated as keys and the second elements are as values.
3. In all other cases, an array is created.
The rules aim to create the best fit between a C++ initializer list and
JSON values. The rationale is as follows:
1. The empty initializer list is written as `{}` which is exactly an empty
JSON object.
JSON object.
2. C++ has now way of describing mapped types other than to list a list of
pairs. As JSON requires that keys must be of type string, rule 2 is the
weakest constraint one can pose on initializer lists to interpret them as
an object.
pairs. As JSON requires that keys must be of type string, rule 2 is the
weakest constraint one can pose on initializer lists to interpret them
as an object.
3. In all other cases, the initializer list could not be interpreted as
JSON object type, so interpreting it as JSON array type is safe.
JSON object type, so interpreting it as JSON array type is safe.
With the rules described above, the following JSON values cannot be
expressed by an initializer list:
......@@ -1554,22 +1675,13 @@ class basic_json
bool type_deduction = true,
value_t manual_type = value_t::array)
{
// the initializer list could describe an object
bool is_an_object = true;
// check if each element is an array with two elements whose first
// element is a string
for (const auto& element : init)
bool is_an_object = std::all_of(init.begin(), init.end(),
[](const basic_json & element)
{
if (not element.is_array() or element.size() != 2
or not element[0].is_string())
{
// we found an element that makes it impossible to use the
// initializer list as object
is_an_object = false;
break;
}
}
return element.is_array() and element.size() == 2 and element[0].is_string();
});
// adjust type if type deduction is not wanted
if (not type_deduction)
......@@ -1593,12 +1705,10 @@ class basic_json
m_type = value_t::object;
m_value = value_t::object;
assert(m_value.object != nullptr);
for (auto& element : init)
std::for_each(init.begin(), init.end(), [this](const basic_json & element)
{
m_value.object->emplace(*(element[0].m_value.string), element[1]);
}
});
}
else
{
......@@ -1606,6 +1716,8 @@ class basic_json
m_type = value_t::array;
m_value.array = create<array_t>(init);
}
assert_invariant();
}
/*!
......@@ -1710,6 +1822,7 @@ class basic_json
: m_type(value_t::array)
{
m_value.array = create<array_t>(cnt, val);
assert_invariant();
}
/*!
......@@ -1730,6 +1843,8 @@ class basic_json
@param[in] first begin of the range to copy from (included)
@param[in] last end of the range to copy from (excluded)
@pre Iterators @a first and @a last must be initialized.
@throw std::domain_error if iterators are not compatible; that is, do not
belong to the same JSON value; example: `"iterators are not compatible"`
@throw std::out_of_range if iterators are for a primitive type (number,
......@@ -1752,14 +1867,20 @@ class basic_json
std::is_same<InputIT, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type)
basic_json(InputIT first, InputIT last)
{
assert(first.m_object != nullptr);
assert(last.m_object != nullptr);
// make sure iterator fits the current value
if (first.m_object != last.m_object)
{
throw std::domain_error("iterators are not compatible");
}
// copy type from first iterator
m_type = first.m_object->m_type;
// check if iterator range is complete for primitive values
switch (m_type)
{
......@@ -1786,35 +1907,30 @@ class basic_json
{
case value_t::number_integer:
{
assert(first.m_object != nullptr);
m_value.number_integer = first.m_object->m_value.number_integer;
break;
}
case value_t::number_unsigned:
{
assert(first.m_object != nullptr);
m_value.number_unsigned = first.m_object->m_value.number_unsigned;
break;
}
case value_t::number_float:
{
assert(first.m_object != nullptr);
m_value.number_float = first.m_object->m_value.number_float;
break;
}
case value_t::boolean:
{
assert(first.m_object != nullptr);
m_value.boolean = first.m_object->m_value.boolean;
break;
}
case value_t::string:
{
assert(first.m_object != nullptr);
m_value = *first.m_object->m_value.string;
break;
}
......@@ -1833,10 +1949,11 @@ class basic_json
default:
{
assert(first.m_object != nullptr);
throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
}
}
assert_invariant();
}
/*!
......@@ -1859,9 +1976,10 @@ class basic_json
@since version 2.0.0
*/
explicit basic_json(std::istream& i, parser_callback_t cb = nullptr)
explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr)
{
*this = parser(i, cb).parse();
assert_invariant();
}
///////////////////////////////////////
......@@ -1893,25 +2011,25 @@ class basic_json
basic_json(const basic_json& other)
: m_type(other.m_type)
{
// check of passed value is valid
other.assert_invariant();
switch (m_type)
{
case value_t::object:
{
assert(other.m_value.object != nullptr);
m_value = *other.m_value.object;
break;
}
case value_t::array:
{
assert(other.m_value.array != nullptr);
m_value = *other.m_value.array;
break;
}
case value_t::string:
{
assert(other.m_value.string != nullptr);
m_value = *other.m_value.string;
break;
}
......@@ -1945,6 +2063,8 @@ class basic_json
break;
}
}
assert_invariant();
}
/*!
......@@ -1969,9 +2089,14 @@ class basic_json
: m_type(std::move(other.m_type)),
m_value(std::move(other.m_value))
{
// check that passed value is valid
other.assert_invariant();
// invalidate payload
other.m_type = value_t::null;
other.m_value = {};
assert_invariant();
}
/*!
......@@ -2004,9 +2129,14 @@ class basic_json
std::is_nothrow_move_assignable<json_value>::value
)
{
// check that passed value is valid
other.assert_invariant();
using std::swap;
swap(m_type, other.m_type);
swap(m_value, other.m_value);
assert_invariant();
return *this;
}
......@@ -2027,6 +2157,8 @@ class basic_json
*/
~basic_json()
{
assert_invariant();
switch (m_type)
{
case value_t::object:
......@@ -2069,19 +2201,20 @@ class basic_json
///////////////////////
/// @name object inspection
/// Functions to inspect the type of a JSON value.
/// @{
/*!
@brief serialization
Serialization function for JSON values. The function tries to mimic
Python's @p json.dumps() function, and currently supports its @p indent
Python's `json.dumps()` function, and currently supports its @a indent
parameter.
@param[in] indent if indent is nonnegative, then array elements and object
@param[in] indent If indent is nonnegative, then array elements and object
members will be pretty-printed with that indent level. An indent level of
0 will only insert newlines. -1 (the default) selects the most compact
representation
`0` will only insert newlines. `-1` (the default) selects the most compact
representation.
@return string containing the serialization of the JSON value
......@@ -2100,6 +2233,12 @@ class basic_json
// fix locale problems
ss.imbue(std::locale(std::locale(), new DecimalSeparator));
// 6, 15 or 16 digits of precision allows round-trip IEEE 754
// string->float->string, string->double->string or string->long
// double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
ss.precision(std::numeric_limits<double>::digits10);
if (indent >= 0)
{
dump(ss, true, static_cast<unsigned int>(indent));
......@@ -2484,7 +2623,6 @@ class basic_json
{
if (is_object())
{
assert(m_value.object != nullptr);
return T(m_value.object->begin(), m_value.object->end());
}
else
......@@ -2498,7 +2636,6 @@ class basic_json
{
if (is_object())
{
assert(m_value.object != nullptr);
return *(m_value.object);
}
else
......@@ -2521,7 +2658,6 @@ class basic_json
if (is_array())
{
T to_vector;
assert(m_value.array != nullptr);
std::transform(m_value.array->begin(), m_value.array->end(),
std::inserter(to_vector, to_vector.end()), [](basic_json i)
{
......@@ -2546,7 +2682,6 @@ class basic_json
if (is_array())
{
std::vector<T> to_vector;
assert(m_value.array != nullptr);
to_vector.reserve(m_value.array->size());
std::transform(m_value.array->begin(), m_value.array->end(),
std::inserter(to_vector, to_vector.end()), [](basic_json i)
......@@ -2571,7 +2706,6 @@ class basic_json
{
if (is_array())
{
assert(m_value.array != nullptr);
return T(m_value.array->begin(), m_value.array->end());
}
else
......@@ -2585,7 +2719,6 @@ class basic_json
{
if (is_array())
{
assert(m_value.array != nullptr);
return *(m_value.array);
}
else
......@@ -2603,7 +2736,6 @@ class basic_json
{
if (is_string())
{
assert(m_value.string != nullptr);
return *m_value.string;
}
else
......@@ -2749,8 +2881,10 @@ class basic_json
template<typename ReferenceType, typename ThisType>
static ReferenceType get_ref_impl(ThisType& obj)
{
// delegate the call to get_ptr<>()
// helper type
using PointerType = typename std::add_pointer<ReferenceType>::type;
// delegate the call to get_ptr<>()
auto ptr = obj.template get_ptr<PointerType>();
if (ptr != nullptr)
......@@ -2767,6 +2901,7 @@ class basic_json
public:
/// @name value access
/// Direct access to the stored value of a JSON value.
/// @{
/*!
......@@ -2817,7 +2952,8 @@ class basic_json
Explicit pointer access to the internally stored JSON value. No copies are
made.
@warning The pointer becomes invalid if the underlying JSON object changes.
@warning The pointer becomes invalid if the underlying JSON object
changes.
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
......@@ -2872,7 +3008,8 @@ class basic_json
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ref number_unsigned_t, or @ref number_float_t.
@ref number_unsigned_t, or @ref number_float_t. Enforced by a static
assertion.
@return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
......@@ -2892,6 +3029,21 @@ class basic_json
, int>::type = 0>
PointerType get_ptr() noexcept
{
// get the type of the PointerType (remove pointer and const)
using pointee_t = typename std::remove_const<typename
std::remove_pointer<typename
std::remove_const<PointerType>::type>::type>::type;
// make sure the type matches the allowed types
static_assert(
std::is_same<object_t, pointee_t>::value
or std::is_same<array_t, pointee_t>::value
or std::is_same<string_t, pointee_t>::value
or std::is_same<boolean_t, pointee_t>::value
or std::is_same<number_integer_t, pointee_t>::value
or std::is_same<number_unsigned_t, pointee_t>::value
or std::is_same<number_float_t, pointee_t>::value
, "incompatible pointer type");
// delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr));
}
......@@ -2907,6 +3059,21 @@ class basic_json
, int>::type = 0>
constexpr const PointerType get_ptr() const noexcept
{
// get the type of the PointerType (remove pointer and const)
using pointee_t = typename std::remove_const<typename
std::remove_pointer<typename
std::remove_const<PointerType>::type>::type>::type;
// make sure the type matches the allowed types
static_assert(
std::is_same<object_t, pointee_t>::value
or std::is_same<array_t, pointee_t>::value
or std::is_same<string_t, pointee_t>::value
or std::is_same<boolean_t, pointee_t>::value
or std::is_same<number_integer_t, pointee_t>::value
or std::is_same<number_unsigned_t, pointee_t>::value
or std::is_same<number_float_t, pointee_t>::value
, "incompatible pointer type");
// delegate the call to get_impl_ptr<>() const
return get_impl_ptr(static_cast<const PointerType>(nullptr));
}
......@@ -2922,7 +3089,7 @@ class basic_json
@tparam ReferenceType reference type; must be a reference to @ref array_t,
@ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or
@ref number_float_t.
@ref number_float_t. Enforced by static assertion.
@return reference to the internally stored JSON value if the requested
reference type @a ReferenceType fits to the JSON value; throws
......@@ -3012,6 +3179,7 @@ class basic_json
////////////////////
/// @name element access
/// Access to the JSON value.
/// @{
/*!
......@@ -3043,7 +3211,6 @@ class basic_json
{
try
{
assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
......@@ -3087,7 +3254,6 @@ class basic_json
{
try
{
assert(m_value.array != nullptr);
return m_value.array->at(idx);
}
catch (std::out_of_range&)
......@@ -3135,7 +3301,6 @@ class basic_json
{
try
{
assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
......@@ -3183,7 +3348,6 @@ class basic_json
{
try
{
assert(m_value.object != nullptr);
return m_value.object->at(key);
}
catch (std::out_of_range&)
......@@ -3230,16 +3394,18 @@ class basic_json
{
m_type = value_t::array;
m_value.array = create<array_t>();
assert_invariant();
}
// operator[] only works for arrays
if (is_array())
{
// fill up array with null values until given idx is reached
assert(m_value.array != nullptr);
for (size_t i = m_value.array->size(); i <= idx; ++i)
// fill up array with null values if given idx is outside range
if (idx >= m_value.array->size())
{
m_value.array->push_back(basic_json());
m_value.array->insert(m_value.array->end(),
idx - m_value.array->size() + 1,
basic_json());
}
return m_value.array->operator[](idx);
......@@ -3274,7 +3440,6 @@ class basic_json
// const operator[] only works for arrays
if (is_array())
{
assert(m_value.array != nullptr);
return m_value.array->operator[](idx);
}
else
......@@ -3317,12 +3482,12 @@ class basic_json
{
m_type = value_t::object;
m_value.object = create<object_t>();
assert_invariant();
}
// operator[] only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
......@@ -3363,7 +3528,6 @@ class basic_json
// const operator[] only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
......@@ -3476,12 +3640,12 @@ class basic_json
{
m_type = value_t::object;
m_value = value_t::object;
assert_invariant();
}
// at only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
return m_value.object->operator[](key);
}
else
......@@ -3523,7 +3687,6 @@ class basic_json
// at only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
assert(m_value.object->find(key) != m_value.object->end());
return m_value.object->find(key)->second;
}
......@@ -3536,8 +3699,8 @@ class basic_json
/*!
@brief access specified object element with default value
Returns either a copy of an object's element at the specified key @a key or
a given default value if no element with key @a key exists.
Returns either a copy of an object's element at the specified key @a key
or a given default value if no element with key @a key exists.
The function is basically equivalent to executing
@code {.cpp}
......@@ -3609,7 +3772,7 @@ class basic_json
/*!
@brief overload for a default value of type const char*
@copydoc basic_json::value()
@copydoc basic_json::value(const typename object_t::key_type&, ValueType) const
*/
string_t value(const typename object_t::key_type& key, const char* default_value) const
{
......@@ -3617,6 +3780,81 @@ class basic_json
}
/*!
@brief access specified object element via JSON Pointer with default value
Returns either a copy of an object's element at the specified key @a key
or a given default value if no element with key @a key exists.
The function is basically equivalent to executing
@code {.cpp}
try {
return at(ptr);
} catch(std::out_of_range) {
return default_value;
}
@endcode
@note Unlike @ref at(const json_pointer&), this function does not throw
if the given key @a key was not found.
@param[in] ptr a JSON pointer to the element to access
@param[in] default_value the value to return if @a ptr found no value
@tparam ValueType type compatible to JSON values, for instance `int` for
JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
JSON arrays. Note the type of the expected value at @a key and the default
value @a default_value must be compatible.
@return copy of the element at key @a key or @a default_value if @a key
is not found
@throw std::domain_error if JSON is not an object; example: `"cannot use
value() with null"`
@complexity Logarithmic in the size of the container.
@liveexample{The example below shows how object elements can be queried
with a default value.,basic_json__value_ptr}
@sa @ref operator[](const json_pointer&) for unchecked access by reference
@since version 2.0.2
*/
template <class ValueType, typename
std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value
, int>::type = 0>
ValueType value(const json_pointer& ptr, ValueType default_value) const
{
// at only works for objects
if (is_object())
{
// if pointer resolves a value, return it or use default value
try
{
return ptr.get_checked(this);
}
catch (std::out_of_range&)
{
return default_value;
}
}
else
{
throw std::domain_error("cannot use value() with " + type_name());
}
}
/*!
@brief overload for a default value of type const char*
@copydoc basic_json::value(const json_pointer&, ValueType) const
*/
string_t value(const json_pointer& ptr, const char* default_value) const
{
return value(ptr, string_t(default_value));
}
/*!
@brief access the first element
Returns a reference to the first element in the container. For a JSON
......@@ -3775,24 +4013,25 @@ class basic_json
if (is_string())
{
delete m_value.string;
AllocatorType<string_t> alloc;
alloc.destroy(m_value.string);
alloc.deallocate(m_value.string, 1);
m_value.string = nullptr;
}
m_type = value_t::null;
assert_invariant();
break;
}
case value_t::object:
{
assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
break;
}
case value_t::array:
{
assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
break;
}
......@@ -3883,17 +4122,19 @@ class basic_json
if (is_string())
{
delete m_value.string;
AllocatorType<string_t> alloc;
alloc.destroy(m_value.string);
alloc.deallocate(m_value.string, 1);
m_value.string = nullptr;
}
m_type = value_t::null;
assert_invariant();
break;
}
case value_t::object:
{
assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
last.m_it.object_iterator);
break;
......@@ -3901,7 +4142,6 @@ class basic_json
case value_t::array:
{
assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
last.m_it.array_iterator);
break;
......@@ -3950,7 +4190,6 @@ class basic_json
// this erase only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
return m_value.object->erase(key);
}
else
......@@ -3993,7 +4232,6 @@ class basic_json
throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
}
assert(m_value.array != nullptr);
m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
}
else
......@@ -4036,7 +4274,6 @@ class basic_json
if (is_object())
{
assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
......@@ -4053,7 +4290,6 @@ class basic_json
if (is_object())
{
assert(m_value.object != nullptr);
result.m_it.object_iterator = m_value.object->find(key);
}
......@@ -4081,7 +4317,6 @@ class basic_json
size_type count(typename object_t::key_type key) const
{
// return 0 for all nonobject types
assert(not is_object() or m_value.object != nullptr);
return is_object() ? m_value.object->count(key) : 0;
}
......@@ -4423,6 +4658,10 @@ class basic_json
object | result of function `object_t::empty()`
array | result of function `array_t::empty()`
@note This function does not return whether a string stored as JSON value
is empty - it returns whether the JSON container itself is empty which is
false in the case of a string.
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their `empty()` functions have constant
complexity.
......@@ -4452,13 +4691,13 @@ class basic_json
case value_t::array:
{
assert(m_value.array != nullptr);
// delegate call to array_t::empty()
return m_value.array->empty();
}
case value_t::object:
{
assert(m_value.object != nullptr);
// delegate call to object_t::empty()
return m_value.object->empty();
}
......@@ -4486,6 +4725,10 @@ class basic_json
object | result of function object_t::size()
array | result of function array_t::size()
@note This function does not return the length of a string stored as JSON
value - it returns the number of elements in the JSON value which is 1 in
the case of a string.
@complexity Constant, as long as @ref array_t and @ref object_t satisfy
the Container concept; that is, their size() functions have constant
complexity.
......@@ -4516,13 +4759,13 @@ class basic_json
case value_t::array:
{
assert(m_value.array != nullptr);
// delegate call to array_t::size()
return m_value.array->size();
}
case value_t::object:
{
assert(m_value.object != nullptr);
// delegate call to object_t::size()
return m_value.object->size();
}
......@@ -4576,13 +4819,13 @@ class basic_json
{
case value_t::array:
{
assert(m_value.array != nullptr);
// delegate call to array_t::max_size()
return m_value.array->max_size();
}
case value_t::object:
{
assert(m_value.object != nullptr);
// delegate call to object_t::max_size()
return m_value.object->max_size();
}
......@@ -4659,21 +4902,18 @@ class basic_json
case value_t::string:
{
assert(m_value.string != nullptr);
m_value.string->clear();
break;
}
case value_t::array:
{
assert(m_value.array != nullptr);
m_value.array->clear();
break;
}
case value_t::object:
{
assert(m_value.object != nullptr);
m_value.object->clear();
break;
}
......@@ -4718,10 +4958,10 @@ class basic_json
{
m_type = value_t::array;
m_value = value_t::array;
assert_invariant();
}
// add element to array (move semantics)
assert(m_value.array != nullptr);
m_value.array->push_back(std::move(val));
// invalidate object
val.m_type = value_t::null;
......@@ -4754,10 +4994,10 @@ class basic_json
{
m_type = value_t::array;
m_value = value_t::array;
assert_invariant();
}
// add element to array
assert(m_value.array != nullptr);
m_value.array->push_back(val);
}
......@@ -4804,10 +5044,10 @@ class basic_json
{
m_type = value_t::object;
m_value = value_t::object;
assert_invariant();
}
// add element to array
assert(m_value.object != nullptr);
m_value.object->insert(val);
}
......@@ -4904,7 +5144,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val);
return result;
}
......@@ -4960,7 +5199,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);
return result;
}
......@@ -5027,7 +5265,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(
pos.m_it.array_iterator,
first.m_it.array_iterator,
......@@ -5075,7 +5312,6 @@ class basic_json
// insert to array and return iterator
iterator result(this);
assert(m_value.array != nullptr);
result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist);
return result;
}
......@@ -5106,6 +5342,7 @@ class basic_json
{
std::swap(m_type, other.m_type);
std::swap(m_value, other.m_value);
assert_invariant();
}
/*!
......@@ -5133,7 +5370,6 @@ class basic_json
// swap only works for arrays
if (is_array())
{
assert(m_value.array != nullptr);
std::swap(*(m_value.array), other);
}
else
......@@ -5167,7 +5403,6 @@ class basic_json
// swap only works for objects
if (is_object())
{
assert(m_value.object != nullptr);
std::swap(*(m_value.object), other);
}
else
......@@ -5201,7 +5436,6 @@ class basic_json
// swap only works for strings
if (is_string())
{
assert(m_value.string != nullptr);
std::swap(*(m_value.string), other);
}
else
......@@ -5288,14 +5522,10 @@ class basic_json
{
case value_t::array:
{
assert(lhs.m_value.array != nullptr);
assert(rhs.m_value.array != nullptr);
return *lhs.m_value.array == *rhs.m_value.array;
}
case value_t::object:
{
assert(lhs.m_value.object != nullptr);
assert(rhs.m_value.object != nullptr);
return *lhs.m_value.object == *rhs.m_value.object;
}
case value_t::null:
......@@ -5304,8 +5534,6 @@ class basic_json
}
case value_t::string:
{
assert(lhs.m_value.string != nullptr);
assert(rhs.m_value.string != nullptr);
return *lhs.m_value.string == *rhs.m_value.string;
}
case value_t::boolean:
......@@ -5478,14 +5706,10 @@ class basic_json
{
case value_t::array:
{
assert(lhs.m_value.array != nullptr);
assert(rhs.m_value.array != nullptr);
return *lhs.m_value.array < *rhs.m_value.array;
}
case value_t::object:
{
assert(lhs.m_value.object != nullptr);
assert(rhs.m_value.object != nullptr);
return *lhs.m_value.object < *rhs.m_value.object;
}
case value_t::null:
......@@ -5494,8 +5718,6 @@ class basic_json
}
case value_t::string:
{
assert(lhs.m_value.string != nullptr);
assert(rhs.m_value.string != nullptr);
return *lhs.m_value.string < *rhs.m_value.string;
}
case value_t::boolean:
......@@ -5637,6 +5859,10 @@ class basic_json
`std::setw(4)` on @a o sets the indentation level to `4` and the
serialization result is the same as calling `dump(4)`.
@note During serializaion, the locale and the precision of the output
stream @a o are changed. The original values are restored when the
function returns.
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
......@@ -5657,14 +5883,23 @@ class basic_json
// reset width to 0 for subsequent calls to this stream
o.width(0);
// fix locale problems
auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
const auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
// set precision
// 6, 15 or 16 digits of precision allows round-trip IEEE 754
// string->float->string, string->double->string or string->long
// double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
const auto old_precision = o.precision(std::numeric_limits<double>::digits10);
// do the actual serialization
j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
// reset locale
// reset locale and precision
o.imbue(old_locale);
o.precision(old_precision);
return o;
}
......@@ -5706,12 +5941,13 @@ class basic_json
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__string__parser_callback_t}
@sa @ref parse(std::istream&, parser_callback_t) for a version that reads
from an input stream
@sa @ref parse(std::istream&, const parser_callback_t) for a version that
reads from an input stream
@since version 1.0.0
*/
static basic_json parse(const string_t& s, parser_callback_t cb = nullptr)
static basic_json parse(const string_t& s,
const parser_callback_t cb = nullptr)
{
return parser(s, cb).parse();
}
......@@ -5735,20 +5971,22 @@ class basic_json
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__istream__parser_callback_t}
@sa @ref parse(const string_t&, parser_callback_t) for a version that
reads from a string
@sa @ref parse(const string_t&, const parser_callback_t) for a version
that reads from a string
@since version 1.0.0
*/
static basic_json parse(std::istream& i, parser_callback_t cb = nullptr)
static basic_json parse(std::istream& i,
const parser_callback_t cb = nullptr)
{
return parser(i, cb).parse();
}
/*!
@copydoc parse(std::istream&, parser_callback_t)
@copydoc parse(std::istream&, const parser_callback_t)
*/
static basic_json parse(std::istream&& i, parser_callback_t cb = nullptr)
static basic_json parse(std::istream&& i,
const parser_callback_t cb = nullptr)
{
return parser(i, cb).parse();
}
......@@ -5771,8 +6009,8 @@ class basic_json
@liveexample{The example below shows how a JSON value is constructed by
reading a serialization from a stream.,operator_deserialize}
@sa parse(std::istream&, parser_callback_t) for a variant with a parser
callback function to filter values while parsing
@sa parse(std::istream&, const parser_callback_t) for a variant with a
parser callback function to filter values while parsing
@since version 1.0.0
*/
......@@ -5800,8 +6038,19 @@ class basic_json
// convenience functions //
///////////////////////////
/// return the type as string
string_t type_name() const noexcept
/*!
@brief return the type as string
Returns the type name as string to be used in error messages - usually to
indicate that a function was called on a wrong JSON type.
@return basically a string representation of a the @ref m_type member
@complexity Constant.
@since version 1.0.0
*/
std::string type_name() const
{
switch (m_type)
{
......@@ -5832,9 +6081,8 @@ class basic_json
*/
static std::size_t extra_space(const string_t& s) noexcept
{
std::size_t result = 0;
for (const auto& c : s)
return std::accumulate(s.begin(), s.end(), size_t{},
[](size_t res, typename string_t::value_type c)
{
switch (c)
{
......@@ -5847,8 +6095,7 @@ class basic_json
case '\t':
{
// from c (1 byte) to \x (2 bytes)
result += 1;
break;
return res + 1;
}
default:
......@@ -5856,14 +6103,15 @@ class basic_json
if (c >= 0x00 and c <= 0x1f)
{
// from c (1 byte) to \uxxxx (6 bytes)
result += 5;
return res + 5;
}
else
{
return res;
}
break;
}
}
}
return result;
});
}
/*!
......@@ -5957,16 +6205,15 @@ class basic_json
{
// convert a number 0..15 to its hex representation
// (0..f)
const auto hexify = [](const int v) -> char
static const char hexify[16] =
{
return (v < 10)
? ('0' + static_cast<char>(v))
: ('a' + static_cast<char>((v - 10) & 0x1f));
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
// print character c as \uxxxx
for (const char m :
{ 'u', '0', '0', hexify(c >> 4), hexify(c & 0x0f)
{ 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f]
})
{
result[++pos] = m;
......@@ -6016,8 +6263,6 @@ class basic_json
{
case value_t::object:
{
assert(m_value.object != nullptr);
if (m_value.object->empty())
{
o << "{}";
......@@ -6058,8 +6303,6 @@ class basic_json
case value_t::array:
{
assert(m_value.array != nullptr);
if (m_value.array->empty())
{
o << "[]";
......@@ -6098,7 +6341,6 @@ class basic_json
case value_t::string:
{
assert(m_value.string != nullptr);
o << string_t("\"") << escape_string(*m_value.string) << "\"";
return;
}
......@@ -6130,13 +6372,7 @@ class basic_json
}
else
{
// Otherwise 6, 15 or 16 digits of precision allows
// round-trip IEEE 754 string->float->string,
// string->double->string or string->long
// double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
o << std::setprecision(std::numeric_limits<double>::digits10)
<< m_value.number_float;
o << m_value.number_float;
}
return;
}
......@@ -6353,6 +6589,12 @@ class basic_json
This class implements a const iterator for the @ref basic_json class. From
this class, the @ref iterator class is derived.
@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:
- [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator):
The iterator that can be moved to point (forward and backward) to any
......@@ -6380,7 +6622,12 @@ class basic_json
/// default constructor
const_iterator() = default;
/// constructor for a given JSON instance
/*!
@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 const_iterator(pointer object) noexcept
: m_object(object)
{
......@@ -6408,40 +6655,53 @@ class basic_json
}
}
/// copy constructor given a nonconst iterator
/*!
@brief copy constructor given a non-const iterator
@param[in] other iterator to copy from
@note It is not checked whether @a other is initialized.
*/
explicit const_iterator(const iterator& other) noexcept
: m_object(other.m_object)
{
assert(m_object != nullptr);
switch (m_object->m_type)
if (m_object != nullptr)
{
case basic_json::value_t::object:
switch (m_object->m_type)
{
m_it.object_iterator = other.m_it.object_iterator;
break;
}
case basic_json::value_t::object:
{
m_it.object_iterator = other.m_it.object_iterator;
break;
}
case basic_json::value_t::array:
{
m_it.array_iterator = other.m_it.array_iterator;
break;
}
case basic_json::value_t::array:
{
m_it.array_iterator = other.m_it.array_iterator;
break;
}
default:
{
m_it.primitive_iterator = other.m_it.primitive_iterator;
break;
default:
{
m_it.primitive_iterator = other.m_it.primitive_iterator;
break;
}
}
}
}
/// copy constructor
/*!
@brief copy constructor
@param[in] other iterator to copy from
@note It is not checked whether @a other is initialized.
*/
const_iterator(const const_iterator& other) noexcept
: m_object(other.m_object), m_it(other.m_it)
{}
/// copy assignment
/*!
@brief copy assignment
@param[in,out] other iterator to copy from
@note It is not checked whether @a other is initialized.
*/
const_iterator& operator=(const_iterator other) noexcept(
std::is_nothrow_move_constructible<pointer>::value and
std::is_nothrow_move_assignable<pointer>::value and
......@@ -6455,7 +6715,10 @@ class basic_json
}
private:
/// set the iterator to the first value
/*!
@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);
......@@ -6464,14 +6727,12 @@ class basic_json
{
case basic_json::value_t::object:
{
assert(m_object->m_value.object != nullptr);
m_it.object_iterator = m_object->m_value.object->begin();
break;
}
case basic_json::value_t::array:
{
assert(m_object->m_value.array != nullptr);
m_it.array_iterator = m_object->m_value.array->begin();
break;
}
......@@ -6491,7 +6752,10 @@ class basic_json
}
}
/// set the iterator past the last value
/*!
@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);
......@@ -6500,14 +6764,12 @@ class basic_json
{
case basic_json::value_t::object:
{
assert(m_object->m_value.object != nullptr);
m_it.object_iterator = m_object->m_value.object->end();
break;
}
case basic_json::value_t::array:
{
assert(m_object->m_value.array != nullptr);
m_it.array_iterator = m_object->m_value.array->end();
break;
}
......@@ -6521,7 +6783,10 @@ class basic_json
}
public:
/// return a reference to the value pointed to by the iterator
/*!
@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);
......@@ -6530,14 +6795,12 @@ class basic_json
{
case basic_json::value_t::object:
{
assert(m_object->m_value.object);
assert(m_it.object_iterator != m_object->m_value.object->end());
return m_it.object_iterator->second;
}
case basic_json::value_t::array:
{
assert(m_object->m_value.array);
assert(m_it.array_iterator != m_object->m_value.array->end());
return *m_it.array_iterator;
}
......@@ -6561,7 +6824,10 @@ class basic_json
}
}
/// dereference the iterator
/*!
@brief dereference the iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
pointer operator->() const
{
assert(m_object != nullptr);
......@@ -6570,14 +6836,12 @@ class basic_json
{
case basic_json::value_t::object:
{
assert(m_object->m_value.object);
assert(m_it.object_iterator != m_object->m_value.object->end());
return &(m_it.object_iterator->second);
}
case basic_json::value_t::array:
{
assert(m_object->m_value.array);
assert(m_it.array_iterator != m_object->m_value.array->end());
return &*m_it.array_iterator;
}
......@@ -6596,7 +6860,10 @@ class basic_json
}
}
/// post-increment (it++)
/*!
@brief post-increment (it++)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator operator++(int)
{
auto result = *this;
......@@ -6604,7 +6871,10 @@ class basic_json
return result;
}
/// pre-increment (++it)
/*!
@brief pre-increment (++it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator& operator++()
{
assert(m_object != nullptr);
......@@ -6613,13 +6883,13 @@ class basic_json
{
case basic_json::value_t::object:
{
++m_it.object_iterator;
std::advance(m_it.object_iterator, 1);
break;
}
case basic_json::value_t::array:
{
++m_it.array_iterator;
std::advance(m_it.array_iterator, 1);
break;
}
......@@ -6633,7 +6903,10 @@ class basic_json
return *this;
}
/// post-decrement (it--)
/*!
@brief post-decrement (it--)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator operator--(int)
{
auto result = *this;
......@@ -6641,7 +6914,10 @@ class basic_json
return result;
}
/// pre-decrement (--it)
/*!
@brief pre-decrement (--it)
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator& operator--()
{
assert(m_object != nullptr);
......@@ -6650,13 +6926,13 @@ class basic_json
{
case basic_json::value_t::object:
{
--m_it.object_iterator;
std::advance(m_it.object_iterator, -1);
break;
}
case basic_json::value_t::array:
{
--m_it.array_iterator;
std::advance(m_it.array_iterator, -1);
break;
}
......@@ -6670,7 +6946,10 @@ class basic_json
return *this;
}
/// comparison: equal
/*!
@brief comparison: equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator==(const const_iterator& other) const
{
// if objects are not the same, the comparison is undefined
......@@ -6700,13 +6979,19 @@ class basic_json
}
}
/// comparison: not equal
/*!
@brief comparison: not equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator!=(const const_iterator& other) const
{
return not operator==(other);
}
/// comparison: smaller
/*!
@brief comparison: smaller
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<(const const_iterator& other) const
{
// if objects are not the same, the comparison is undefined
......@@ -6736,25 +7021,37 @@ class basic_json
}
}
/// comparison: less than or equal
/*!
@brief comparison: less than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator<=(const const_iterator& other) const
{
return not other.operator < (*this);
}
/// comparison: greater than
/*!
@brief comparison: greater than
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>(const const_iterator& other) const
{
return not operator<=(other);
}
/// comparison: greater than or equal
/*!
@brief comparison: greater than or equal
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
bool operator>=(const const_iterator& other) const
{
return not operator<(other);
}
/// add to iterator
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator& operator+=(difference_type i)
{
assert(m_object != nullptr);
......@@ -6768,7 +7065,7 @@ class basic_json
case basic_json::value_t::array:
{
m_it.array_iterator += i;
std::advance(m_it.array_iterator, i);
break;
}
......@@ -6782,13 +7079,19 @@ class basic_json
return *this;
}
/// subtract from iterator
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator& operator-=(difference_type i)
{
return operator+=(-i);
}
/// add to iterator
/*!
@brief add to iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator operator+(difference_type i)
{
auto result = *this;
......@@ -6796,7 +7099,10 @@ class basic_json
return result;
}
/// subtract from iterator
/*!
@brief subtract from iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
const_iterator operator-(difference_type i)
{
auto result = *this;
......@@ -6804,7 +7110,10 @@ class basic_json
return result;
}
/// return difference
/*!
@brief return difference
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
difference_type operator-(const const_iterator& other) const
{
assert(m_object != nullptr);
......@@ -6828,7 +7137,10 @@ class basic_json
}
}
/// access to successor
/*!
@brief access to successor
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference operator[](difference_type n) const
{
assert(m_object != nullptr);
......@@ -6842,7 +7154,7 @@ class basic_json
case basic_json::value_t::array:
{
return *(m_it.array_iterator + n);
return *std::next(m_it.array_iterator, n);
}
case basic_json::value_t::null:
......@@ -6864,7 +7176,10 @@ class basic_json
}
}
/// return the key of an object iterator
/*!
@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);
......@@ -6879,7 +7194,10 @@ class basic_json
}
}
/// return the value of an iterator
/*!
@brief return the value of an iterator
@pre The iterator is initialized; i.e. `m_object != nullptr`.
*/
reference value() const
{
return operator*();
......@@ -7181,7 +7499,7 @@ class basic_json
explicit lexer(const string_t& s) noexcept
: m_stream(nullptr), m_buffer(s)
{
m_content = reinterpret_cast<const lexer_char_t*>(s.c_str());
m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
assert(m_content != nullptr);
m_start = m_cursor = m_content;
m_limit = m_content + s.size();
......@@ -7192,7 +7510,7 @@ class basic_json
: m_stream(s), m_buffer()
{
assert(m_stream != nullptr);
getline(*m_stream, m_buffer);
std::getline(*m_stream, m_buffer);
m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
assert(m_content != nullptr);
m_start = m_cursor = m_content;
......@@ -7207,24 +7525,32 @@ class basic_json
lexer operator=(const lexer&) = delete;
/*!
@brief create a string from a Unicode code point
@brief create a string from one or two Unicode code points
There are two cases: (1) @a codepoint1 is in the Basic Multilingual
Plane (U+0000 through U+FFFF) and @a codepoint2 is 0, or (2)
@a codepoint1 and @a codepoint2 are a UTF-16 surrogate pair to
represent a code point above U+FFFF.
@param[in] codepoint1 the code point (can be high surrogate)
@param[in] codepoint2 the code point (can be low surrogate or 0)
@return string representation of the code point
@return string representation of the code point; the length of the
result string is between 1 and 4 characters.
@throw std::out_of_range if code point is > 0x10ffff; example: `"code
points above 0x10FFFF are invalid"`
@throw std::invalid_argument if the low surrogate is invalid; example:
`""missing or wrong low surrogate""`
@complexity Constant.
@see <http://en.wikipedia.org/wiki/UTF-8#Sample_code>
*/
static string_t to_unicode(const std::size_t codepoint1,
const std::size_t codepoint2 = 0)
{
// calculate the codepoint from the given code points
// calculate the code point from the given code points
std::size_t codepoint = codepoint1;
// check if codepoint1 is a high surrogate
......@@ -7286,7 +7612,7 @@ class basic_json
}
/// return name of values of type token_type (only used for errors)
static std::string token_type_name(token_type t)
static std::string token_type_name(const token_type t)
{
switch (t)
{
......@@ -7335,79 +7661,95 @@ class basic_json
function consists of a large block of code with `goto` jumps.
@return the class of the next token read from the buffer
@complexity Linear in the length of the input.\n
Proposition: The loop below will always terminate for finite input.\n
Proof (by contradiction): Assume a finite input. To loop forever, the
loop must never hit code with a `break` statement. The only code
snippets without a `break` statement are the continue statements for
whitespace and byte-order-marks. To loop forever, the input must be an
infinite sequence of whitespace or byte-order-marks. This contradicts
the assumption of finite input, q.e.d.
*/
token_type scan() noexcept
{
// pointer for backtracking information
m_marker = nullptr;
while (true)
{
// pointer for backtracking information
m_marker = nullptr;
// remember the begin of the token
m_start = m_cursor;
assert(m_start != nullptr);
/*!re2c
re2c:define:YYCTYPE = lexer_char_t;
re2c:define:YYCURSOR = m_cursor;
re2c:define:YYLIMIT = m_limit;
re2c:define:YYMARKER = m_marker;
re2c:define:YYFILL = "yyfill(); // LCOV_EXCL_LINE";
re2c:yyfill:parameter = 0;
re2c:indent:string = " ";
re2c:indent:top = 1;
re2c:labelprefix = "basic_json_parser_";
// ignore whitespace
ws = [ \t\n\r]+;
ws { continue; }
// ignore byte-order-mark
bom = "\xEF\xBB\xBF";
bom { continue; }
// structural characters
"[" { last_token_type = token_type::begin_array; break; }
"]" { last_token_type = token_type::end_array; break; }
"{" { last_token_type = token_type::begin_object; break; }
"}" { last_token_type = token_type::end_object; break; }
"," { last_token_type = token_type::value_separator; break; }
":" { last_token_type = token_type::name_separator; break; }
// literal names
"null" { last_token_type = token_type::literal_null; break; }
"true" { last_token_type = token_type::literal_true; break; }
"false" { last_token_type = token_type::literal_false; break; }
// number
decimal_point = ".";
digit = [0-9];
digit_1_9 = [1-9];
e = "e" | "E";
minus = "-";
plus = "+";
zero = "0";
exp = e (minus | plus)? digit+;
frac = decimal_point digit+;
int = (zero | digit_1_9 digit*);
number = minus? int frac? exp?;
number { last_token_type = token_type::value_number; break; }
// remember the begin of the token
m_start = m_cursor;
assert(m_start != nullptr);
// string
quotation_mark = "\"";
escape = "\\";
unescaped = [^"\\\x00-\x1f];
single_escaped = "\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t";
unicode_escaped = "u" [0-9a-fA-F]{4};
escaped = escape (single_escaped | unicode_escaped);
char = unescaped | escaped;
string = quotation_mark char* quotation_mark;
string { last_token_type = token_type::value_string; break; }
// end of file
"\000" { last_token_type = token_type::end_of_input; break; }
// anything else is an error
. { last_token_type = token_type::parse_error; break; }
*/
}
/*!re2c
re2c:define:YYCTYPE = lexer_char_t;
re2c:define:YYCURSOR = m_cursor;
re2c:define:YYLIMIT = m_limit;
re2c:define:YYMARKER = m_marker;
re2c:define:YYFILL = "yyfill(); // LCOV_EXCL_LINE";
re2c:yyfill:parameter = 0;
re2c:indent:string = " ";
re2c:indent:top = 1;
re2c:labelprefix = "basic_json_parser_";
// ignore whitespace
ws = [ \t\n\r]+;
ws { return scan(); }
// ignore byte-order-mark
bom = "\xEF\xBB\xBF";
bom { return scan(); }
// structural characters
"[" { return token_type::begin_array; }
"]" { return token_type::end_array; }
"{" { return token_type::begin_object; }
"}" { return token_type::end_object; }
"," { return token_type::value_separator; }
":" { return token_type::name_separator; }
// literal names
"null" { return token_type::literal_null; }
"true" { return token_type::literal_true; }
"false" { return token_type::literal_false; }
// number
decimal_point = [.];
digit = [0-9];
digit_1_9 = [1-9];
e = [eE];
minus = [-];
plus = [+];
zero = [0];
exp = e (minus|plus)? digit+;
frac = decimal_point digit+;
int = (zero|digit_1_9 digit*);
number = minus? int frac? exp?;
number { return token_type::value_number; }
// string
quotation_mark = ["];
escape = [\\];
unescaped = [^"\\\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F];
single_escaped = ["\\/bfnrt];
unicode_escaped = [u][0-9a-fA-F]{4};
escaped = escape (single_escaped | unicode_escaped);
char = unescaped | escaped;
string = quotation_mark char* quotation_mark;
string { return token_type::value_string; }
// end of file
'\000' { return token_type::end_of_input; }
// anything else is an error
. { return token_type::parse_error; }
*/
return last_token_type;
}
/// append data from the stream to the internal buffer
......@@ -7437,7 +7779,7 @@ class basic_json
}
/// return string representation of last read token
string_t get_token() const
string_t get_token_string() const
{
assert(m_start != nullptr);
return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start),
......@@ -7462,12 +7804,49 @@ class basic_json
of the construction of the values.
2. Unescaped characters are copied as is.
@pre `m_cursor - m_start >= 2`, meaning the length of the last token
is at least 2 bytes which is trivially true for any string (which
consists of at least two quotes).
" c1 c2 c3 ... "
^ ^
m_start m_cursor
@complexity Linear in the length of the string.\n
Lemma: The loop body will always terminate.\n
Proof (by contradiction): Assume the loop body does not terminate. As
the loop body does not contain another loop, one of the called
functions must never return. The called functions are `std::strtoul`
and to_unicode. Neither function can loop forever, so the loop body
will never loop forever which contradicts the assumption that the loop
body does not terminate, q.e.d.\n
Lemma: The loop condition for the for loop is eventually false.\n
Proof (by contradiction): Assume the loop does not terminate. Due to
the above lemma, this can only be due to a tautological loop
condition; that is, the loop condition i < m_cursor - 1 must always be
true. Let x be the change of i for any loop iteration. Then
m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This
can be rephrased to m_cursor - m_start - 2 > x. With the
precondition, we x <= 0, meaning that the loop condition holds
indefinitly if i is always decreased. However, observe that the value
of i is strictly increasing with each iteration, as it is incremented
by 1 in the iteration expression and never decremented inside the loop
body. Hence, the loop condition will eventually be false which
contradicts the assumption that the loop condition is a tautology,
q.e.d.
@return string value of current token without opening and closing
quotes
@throw std::out_of_range if to_unicode fails
*/
string_t get_string() const
{
assert(m_cursor - m_start >= 2);
string_t result;
result.reserve(static_cast<size_t>(m_cursor - m_start - 2));
......@@ -7583,11 +7962,6 @@ class basic_json
the number
@return the floating point number
@bug This function uses `std::strtof`, `std::strtod`, or `std::strtold`
which use the current C locale to determine which character is used as
decimal point character. This may yield to parse errors if the locale
does not used `.`.
*/
long double str_to_float_t(long double* /* type */, char** endptr) const
{
......@@ -7705,7 +8079,7 @@ class basic_json
if (type != value_t::number_float)
{
// multiply last value by ten and add the new digit
auto temp = value * 10 + *curptr - 0x30;
auto temp = value * 10 + *curptr - '0';
// test for overflow
if (temp < value || temp > max)
......@@ -7755,6 +8129,8 @@ class basic_json
const lexer_char_t* m_cursor = nullptr;
/// pointer to the end of the buffer
const lexer_char_t* m_limit = nullptr;
/// the last token type
token_type last_token_type = token_type::end_of_input;
};
/*!
......@@ -7766,7 +8142,7 @@ class basic_json
{
public:
/// constructor for strings
parser(const string_t& s, parser_callback_t cb = nullptr) noexcept
parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
: callback(cb), m_lexer(s)
{
// read first token
......@@ -7774,7 +8150,7 @@ class basic_json
}
/// a parser reading from an input stream
parser(std::istream& _is, parser_callback_t cb = nullptr) noexcept
parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept
: callback(cb), m_lexer(&_is)
{
// read first token
......@@ -7785,12 +8161,13 @@ class basic_json
basic_json parse()
{
basic_json result = parse_internal(true);
result.assert_invariant();
expect(lexer::token_type::end_of_input);
// return parser result and replace it with null in case the
// top-level value was discarded by the callback function
return result.is_discarded() ? basic_json() : result;
return result.is_discarded() ? basic_json() : std::move(result);
}
private:
......@@ -7807,7 +8184,7 @@ class basic_json
{
// explicitly set result to object to cope with {}
result.m_type = value_t::object;
result.m_value = json_value(value_t::object);
result.m_value = value_t::object;
}
// read next token
......@@ -7885,7 +8262,7 @@ class basic_json
{
// explicitly set result to object to cope with []
result.m_type = value_t::array;
result.m_value = json_value(value_t::array);
result.m_value = value_t::array;
}
// read next token
......@@ -7998,7 +8375,8 @@ class basic_json
if (t != last_token)
{
std::string error_msg = "parse error - unexpected ";
error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") :
error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() +
"'") :
lexer::token_type_name(last_token));
error_msg += "; expected " + lexer::token_type_name(t);
throw std::invalid_argument(error_msg);
......@@ -8010,7 +8388,8 @@ class basic_json
if (t == last_token)
{
std::string error_msg = "parse error - unexpected ";
error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token() + "'") :
error_msg += (last_token == lexer::token_type::parse_error ? ("'" + m_lexer.get_token_string() +
"'") :
lexer::token_type_name(last_token));
throw std::invalid_argument(error_msg);
}
......@@ -8020,7 +8399,7 @@ class basic_json
/// current level of recursion
int depth = 0;
/// callback function
parser_callback_t callback;
const parser_callback_t callback = nullptr;
/// the type of the last read token
typename lexer::token_type last_token = lexer::token_type::uninitialized;
/// the lexer
......@@ -8088,14 +8467,12 @@ class basic_json
*/
std::string to_string() const noexcept
{
std::string result;
for (const auto& reference_token : reference_tokens)
return std::accumulate(reference_tokens.begin(),
reference_tokens.end(), std::string{},
[](const std::string & a, const std::string & b)
{
result += "/" + escape(reference_token);
}
return result;
return a + "/" + escape(b);
});
}
/// @copydoc to_string()
......@@ -8138,6 +8515,8 @@ class basic_json
/*!
@brief create and return a reference to the pointed to value
@complexity Linear in the number of reference tokens.
*/
reference get_and_create(reference j) const
{
......@@ -8459,7 +8838,7 @@ class basic_json
@param[in,out] s the string to manipulate
@param[in] f the substring to replace with @a t
@param[out] t the string to replace @a f
@param[in] t the string to replace @a f
@return The string @a s where all occurrences of @a f are replaced
with @a t.
......@@ -9315,7 +9694,7 @@ struct hash<nlohmann::json>
@brief user-defined string literal for JSON values
This operator implements a user-defined string literal for JSON objects. It
can be used by adding \p "_json" to a string literal and returns a JSON object
can be used by adding `"_json"` to a string literal and returns a JSON object
if no parse error occurred.
@param[in] s a string representation of a JSON object
......@@ -9331,6 +9710,13 @@ inline nlohmann::json operator "" _json(const char* s, std::size_t)
/*!
@brief user-defined string literal for JSON pointer
This operator implements a user-defined string literal for JSON Pointers. It
can be used by adding `"_json"` to a string literal and returns a JSON pointer
object if no parse error occurred.
@param[in] s a string representation of a JSON Pointer
@return a JSON pointer object
@since version 2.0.0
*/
inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t)
......
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (fuzz test support)
| | |__ | | | | | | version 2.0.0
| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Run "make fuzz_testing" and follow the instructions.
......
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
| | |__ | | | | | | version 2.0.0
| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
......@@ -1685,6 +1685,28 @@ TEST_CASE("object inspection")
json j_discarded(json::value_t::discarded);
CHECK(j_discarded.dump() == "<discarded>");
}
SECTION("check that precision is reset after serialization")
{
// create stringstream and set precision
std::stringstream ss;
ss.precision(3);
ss << 3.141592653589793 << std::fixed;
CHECK(ss.str() == "3.14");
// reset stringstream
ss.str(std::string());
// use stringstream for JSON serialization
json j_number = 3.141592653589793;
ss << j_number;
// check that precision has been overridden during serialization
CHECK(ss.str() == "3.141592653589793");
// check that precision has been restored
CHECK(ss.precision() == 3);
}
}
SECTION("return the type of the object (explicit)")
......@@ -3746,123 +3768,254 @@ TEST_CASE("element access")
SECTION("access specified element with default value")
{
SECTION("access existing value")
{
CHECK(j.value("integer", 2) == 1);
CHECK(j.value("integer", 1.0) == Approx(1));
CHECK(j.value("unsigned", 2) == 1u);
CHECK(j.value("unsigned", 1.0) == Approx(1u));
CHECK(j.value("null", json(1)) == json());
CHECK(j.value("boolean", false) == true);
CHECK(j.value("string", "bar") == "hello world");
CHECK(j.value("string", std::string("bar")) == "hello world");
CHECK(j.value("floating", 12.34) == Approx(42.23));
CHECK(j.value("floating", 12) == 42);
CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object()));
CHECK(j.value("array", json({10, 100})) == json({1, 2, 3}));
CHECK(j_const.value("integer", 2) == 1);
CHECK(j_const.value("integer", 1.0) == Approx(1));
CHECK(j_const.value("unsigned", 2) == 1u);
CHECK(j_const.value("unsigned", 1.0) == Approx(1u));
CHECK(j_const.value("boolean", false) == true);
CHECK(j_const.value("string", "bar") == "hello world");
CHECK(j_const.value("string", std::string("bar")) == "hello world");
CHECK(j_const.value("floating", 12.34) == Approx(42.23));
CHECK(j_const.value("floating", 12) == 42);
CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object()));
CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3}));
}
SECTION("access non-existing value")
{
CHECK(j.value("_", 2) == 2);
CHECK(j.value("_", 2u) == 2u);
CHECK(j.value("_", false) == false);
CHECK(j.value("_", "bar") == "bar");
CHECK(j.value("_", 12.34) == Approx(12.34));
CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j.value("_", json({10, 100})) == json({10, 100}));
CHECK(j_const.value("_", 2) == 2);
CHECK(j_const.value("_", 2u) == 2u);
CHECK(j_const.value("_", false) == false);
CHECK(j_const.value("_", "bar") == "bar");
CHECK(j_const.value("_", 12.34) == Approx(12.34));
CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j_const.value("_", json({10, 100})) == json({10, 100}));
SECTION("given a key")
{
SECTION("access existing value")
{
CHECK(j.value("integer", 2) == 1);
CHECK(j.value("integer", 1.0) == Approx(1));
CHECK(j.value("unsigned", 2) == 1u);
CHECK(j.value("unsigned", 1.0) == Approx(1u));
CHECK(j.value("null", json(1)) == json());
CHECK(j.value("boolean", false) == true);
CHECK(j.value("string", "bar") == "hello world");
CHECK(j.value("string", std::string("bar")) == "hello world");
CHECK(j.value("floating", 12.34) == Approx(42.23));
CHECK(j.value("floating", 12) == 42);
CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object()));
CHECK(j.value("array", json({10, 100})) == json({1, 2, 3}));
CHECK(j_const.value("integer", 2) == 1);
CHECK(j_const.value("integer", 1.0) == Approx(1));
CHECK(j_const.value("unsigned", 2) == 1u);
CHECK(j_const.value("unsigned", 1.0) == Approx(1u));
CHECK(j_const.value("boolean", false) == true);
CHECK(j_const.value("string", "bar") == "hello world");
CHECK(j_const.value("string", std::string("bar")) == "hello world");
CHECK(j_const.value("floating", 12.34) == Approx(42.23));
CHECK(j_const.value("floating", 12) == 42);
CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object()));
CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3}));
}
SECTION("access non-existing value")
{
CHECK(j.value("_", 2) == 2);
CHECK(j.value("_", 2u) == 2u);
CHECK(j.value("_", false) == false);
CHECK(j.value("_", "bar") == "bar");
CHECK(j.value("_", 12.34) == Approx(12.34));
CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j.value("_", json({10, 100})) == json({10, 100}));
CHECK(j_const.value("_", 2) == 2);
CHECK(j_const.value("_", 2u) == 2u);
CHECK(j_const.value("_", false) == false);
CHECK(j_const.value("_", "bar") == "bar");
CHECK(j_const.value("_", 12.34) == Approx(12.34));
CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j_const.value("_", json({10, 100})) == json({10, 100}));
}
SECTION("access on non-object type")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null");
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean");
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string");
}
SECTION("array")
{
json j_nonobject(json::value_t::array);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array");
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
}
}
SECTION("access on non-object type")
SECTION("given a JSON pointer")
{
SECTION("null")
SECTION("access existing value")
{
json j_nonobject(json::value_t::null);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null");
}
CHECK(j.value("/integer"_json_pointer, 2) == 1);
CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1));
CHECK(j.value("/unsigned"_json_pointer, 2) == 1u);
CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
CHECK(j.value("/null"_json_pointer, json(1)) == json());
CHECK(j.value("/boolean"_json_pointer, false) == true);
CHECK(j.value("/string"_json_pointer, "bar") == "hello world");
CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world");
CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23));
CHECK(j.value("/floating"_json_pointer, 12) == 42);
CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean");
CHECK(j_const.value("/integer"_json_pointer, 2) == 1);
CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1));
CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u);
CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
CHECK(j_const.value("/boolean"_json_pointer, false) == true);
CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world");
CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world");
CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23));
CHECK(j_const.value("/floating"_json_pointer, 12) == 42);
CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
}
SECTION("string")
SECTION("access non-existing value")
{
json j_nonobject(json::value_t::string);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string");
}
CHECK(j.value("/not/existing"_json_pointer, 2) == 2);
CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u);
CHECK(j.value("/not/existing"_json_pointer, false) == false);
CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar");
CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
SECTION("array")
{
json j_nonobject(json::value_t::array);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array");
CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2);
CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u);
CHECK(j_const.value("/not/existing"_json_pointer, false) == false);
CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar");
CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
}
SECTION("number (integer)")
SECTION("access on non-object type")
{
json j_nonobject(json::value_t::number_integer);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
SECTION("null")
{
json j_nonobject(json::value_t::null);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
"cannot use value() with boolean");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
SECTION("string")
{
json j_nonobject(json::value_t::string);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
"cannot use value() with string");
}
SECTION("array")
{
json j_nonobject(json::value_t::array);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array");
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
"cannot use value() with number");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
"cannot use value() with number");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
"cannot use value() with number");
}
}
}
}
......@@ -9694,6 +9847,39 @@ TEST_CASE("parser class")
CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'");
// improve code coverage
CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument);
// unescaped control characters
CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument);
}
SECTION("escaped")
......@@ -10304,257 +10490,274 @@ TEST_CASE("parser class")
CHECK(j_empty_array == json());
}
}
SECTION("copy constructor")
{
json::string_t* s = new json::string_t("[1,2,3,4]");
json::parser p(*s);
delete s;
CHECK(p.parse() == json({1, 2, 3, 4}));
}
}
TEST_CASE("README", "[hide]")
{
{
// create an empty structure (null)
json j;
// redirect std::cout for the README file
auto old_cout_buffer = std::cout.rdbuf();
std::ostringstream new_stream;
std::cout.rdbuf(new_stream.rdbuf());
{
// 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 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 Boolean that is stored as bool
j["happy"] = true;
// add a string that is stored as std::string
j["name"] = "Niels";
// add a string that is stored as std::string
j["name"] = "Niels";
// add another null object by passing nullptr
j["nothing"] = nullptr;
// add another null object by passing nullptr
j["nothing"] = nullptr;
// add an object inside the object
j["answer"]["everything"] = 42;
// 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 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} };
// 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},
// instead, you could also write (which looks very similar to the JSON above)
json j2 =
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99}
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99}
}
}
}
};
}
};
}
{
// ways to express the empty array []
json empty_array_implicit = {{}};
json empty_array_explicit = json::array();
{
// 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 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}) };
}
// 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}) };
}
{
// create object from string literal
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
{
// create object from string literal
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
// or even nicer with a raw string literal
auto j2 = R"(
// or even nicer with a raw string literal
auto j2 = R"(
{
"happy": true,
"pi": 3.141
}
)"_json;
// or explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
// explicit conversion to string
std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141}
// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump(4) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
// or explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
std::cout << std::setw(2) << j << std::endl;
}
// explicit conversion to string
std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141}
{
// create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);
// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump(4) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it)
{
std::cout << *it << '\n';
std::cout << std::setw(2) << j << std::endl;
}
// range-based for
for (auto element : j)
{
std::cout << element << '\n';
}
// 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);
// 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
// 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
// comparison
j == "[\"foo\", 1, true]"_json; // true
// create an object
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;
// 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"
// find an entry
if (o.find("foo") != o.end())
{
// there is an entry with key "foo"
}
}
}
{
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// [1, 2, 3, 4]
{
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// [1, 2, 3, 4]
std::deque<float> c_deque {1.2f, 2.3f, 3.4f, 5.6f};
json j_deque(c_deque);
// [1.2, 2.3, 3.4, 5.6]
std::deque<float> c_deque {1.2f, 2.3f, 3.4f, 5.6f};
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::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::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::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::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::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::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"]
}
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"]
}
{
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::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.2f}, {"two", 2.3f}, {"three", 3.4f} };
json j_umap(c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}
std::unordered_map<const char*, float> c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} };
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::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}
}
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}
}
{
// strings
std::string s1 = "Hello, world!";
json js = s1;
std::string s2 = js;
{
// strings
std::string s1 = "Hello, world!";
json js = s1;
std::string s2 = js;
// Booleans
bool b1 = true;
json jb = b1;
bool b2 = jb;
// Booleans
bool b1 = true;
json jb = b1;
bool b2 = jb;
// numbers
int i = 42;
json jn = i;
double f = jn;
// numbers
int i = 42;
json jn = i;
double f = jn;
// etc.
// etc.
std::string vs = js.get<std::string>();
bool vb = jb.get<bool>();
int vi = jn.get<int>();
std::string vs = js.get<std::string>();
bool vb = jb.get<bool>();
int vi = jn.get<int>();
// etc.
}
// etc.
}
{
// a JSON value
json j_original = R"({
{
// a JSON value
json j_original = R"({
"baz": ["one", "two", "three"],
"foo": "bar"
})"_json;
// access members with a JSON pointer (RFC 6901)
j_original["/baz/1"_json_pointer];
// "two"
// access members with a JSON pointer (RFC 6901)
j_original["/baz/1"_json_pointer];
// "two"
// a JSON patch (RFC 6902)
json j_patch = R"([
// a JSON patch (RFC 6902)
json j_patch = R"([
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
])"_json;
// apply the patch
json j_result = j_original.patch(j_patch);
// {
// "baz": "boo",
// "hello": ["world"]
// }
// calculate a JSON patch from two JSON values
json::diff(j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op":"remove","path":"/hello" },
// { "op":"add","path":"/foo","value":"bar" }
// ]
// apply the patch
json j_result = j_original.patch(j_patch);
// {
// "baz": "boo",
// "hello": ["world"]
// }
// calculate a JSON patch from two JSON values
json::diff(j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op":"remove","path":"/hello" },
// { "op":"add","path":"/foo","value":"bar" }
// ]
}
// restore old std::cout
std::cout.rdbuf(old_cout_buffer);
}
}
......@@ -12082,19 +12285,23 @@ TEST_CASE("RFC 7159 examples")
TEST_CASE("Unicode", "[hide]")
{
SECTION("full enumeration of Unicode codepoints")
SECTION("full enumeration of Unicode code points")
{
// create a string from a codepoint
auto codepoint_to_unicode = [](std::size_t cp)
// create an escaped string from a code point
const auto codepoint_to_unicode = [](std::size_t cp)
{
char* buffer = new char[10];
sprintf(buffer, "\\u%04lx", cp);
std::string result(buffer);
delete[] buffer;
return result;
// copd points are represented as a six-character sequence: a
// reverse solidus, followed by the lowercase letter u, followed
// by four hexadecimal digits that encode the character's code
// point
std::stringstream ss;
ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp;
return ss.str();
};
// generate all codepoints
// generate all UTF-8 code points; in total, 1112064 code points are
// generated: 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp)
{
// The Unicode standard permanently reserves these code point
......@@ -12104,34 +12311,57 @@ TEST_CASE("Unicode", "[hide]")
// no UTF forms, including UTF-16, can encode these code points.
if (cp >= 0xD800u and cp <= 0xDFFFu)
{
// if we would not skip these code points, we would get a
// "missing low surrogate" exception
continue;
}
std::string res;
// string to store the code point as in \uxxxx format
std::string escaped_string;
// string to store the code point as unescaped character sequence
std::string unescaped_string;
if (cp < 0x10000u)
{
// codepoint can be represented with 16 bit
res += codepoint_to_unicode(cp);
// code points in the Basic Multilingual Plane can be
// represented with one \\uxxxx sequence
escaped_string = codepoint_to_unicode(cp);
// All Unicode characters may be placed within the quotation
// marks, except for the characters that must be escaped:
// quotation mark, reverse solidus, and the control characters
// (U+0000 through U+001F); we ignore these code points as
// they are checked with codepoint_to_unicode.
if (cp > 0x1f and cp != 0x22 and cp != 0x5c)
{
unescaped_string = json::lexer::to_unicode(cp);
}
}
else
{
// codepoint can be represented with a pair
res += codepoint_to_unicode(0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu));
res += codepoint_to_unicode(0xdc00u + ((cp - 0x10000u) & 0x3ffu));
// To escape an extended character that is not in the Basic
// Multilingual Plane, the character is represented as a
// 12-character sequence, encoding the UTF-16 surrogate pair
const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
escaped_string = codepoint_to_unicode(codepoint1);
escaped_string += codepoint_to_unicode(codepoint2);
unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2);
}
try
{
json j1, j2;
CHECK_NOTHROW(j1 = json::parse("\"" + res + "\""));
CHECK_NOTHROW(j2 = json::parse(j1.dump()));
CHECK(j1 == j2);
}
catch (std::invalid_argument)
{
// we ignore parsing errors
}
// all other code points are valid and must not yield parse errors
CAPTURE(cp);
CAPTURE(escaped_string);
CAPTURE(unescaped_string);
json j1, j2, j3, j4;
CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\""));
CHECK_NOTHROW(j2 = json::parse(j1.dump()));
CHECK(j1 == j2);
CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\""));
CHECK_NOTHROW(j4 = json::parse(j3.dump()));
CHECK(j3 == j4);
}
}
......@@ -12144,6 +12374,8 @@ TEST_CASE("Unicode", "[hide]")
CHECK_NOTHROW(j << f);
// the array has 1112064 + 1 elemnts (a terminating "null" value)
// Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
CHECK(j.size() == 1112065);
SECTION("check JSON Pointers")
......@@ -14128,6 +14360,19 @@ TEST_CASE("regression tests")
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("issue #283 - value() does not work with _json_pointer types")
{
json j =
{
{"object", {{"key1", 1}, {"key2", 2}}},
};
int at_integer = j.at("/object/key2"_json_pointer);
int val_integer = j.value("/object/key2"_json_pointer, 0);
CHECK(at_integer == val_integer);
}
}
// special test case to check if memory is leaked if constructor throws
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment