Merge branch 'develop' into coverity_scan

This commit is contained in:
Niels 2016-07-19 22:49:17 +02:00
commit ea19be1736
8 changed files with 1231 additions and 1049 deletions

View file

@ -3,7 +3,24 @@ All notable changes to this project will be documented in this file. This projec
## [Unreleased](https://github.com/nlohmann/json/tree/HEAD) ## [Unreleased](https://github.com/nlohmann/json/tree/HEAD)
[Full Changelog](https://github.com/nlohmann/json/compare/v1.1.0...HEAD) [Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...HEAD)
- 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)
- fixed a tiny typo [\#271](https://github.com/nlohmann/json/pull/271) ([thelostt](https://github.com/thelostt))
## [v2.0.0](https://github.com/nlohmann/json/releases/tag/v2.0.0) (2016-06-23)
[Full Changelog](https://github.com/nlohmann/json/compare/v1.1.0...v2.0.0)
- concatenate objects [\#252](https://github.com/nlohmann/json/issues/252) - concatenate objects [\#252](https://github.com/nlohmann/json/issues/252)
- Unit test fails when doing a CMake out-of-tree build [\#241](https://github.com/nlohmann/json/issues/241) - Unit test fails when doing a CMake out-of-tree build [\#241](https://github.com/nlohmann/json/issues/241)

View file

@ -1,4 +1,4 @@
![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif) [![JSON for Modern C++](https://raw.githubusercontent.com/nlohmann/json/master/doc/json.gif)](https://github.com/nlohmann/json/releases)
[![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json)
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
@ -291,7 +291,7 @@ json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"] // maybe ["one", "two", "one", "four"]
``` ```
Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys are can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container. Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
```cpp ```cpp
std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} }; std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
@ -323,7 +323,7 @@ json j_original = R"({
})"_json; })"_json;
// access members with a JSON pointer (RFC 6901) // access members with a JSON pointer (RFC 6901)
j_original["/baz/2"_json_pointer]; j_original["/baz/1"_json_pointer];
// "two" // "two"
// a JSON patch (RFC 6902) // a JSON patch (RFC 6902)
@ -344,8 +344,8 @@ json j_result = j_original.patch(j_patch);
json::diff(j_result, j_original); json::diff(j_result, j_original);
// [ // [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op":"remove","path":"/hello" }, // { "op": "remove","path": "/hello" },
// { "op":"add","path":"/foo","value":"bar" } // { "op": "add", "path": "/foo", "value": "bar" }
// ] // ]
``` ```
@ -390,7 +390,7 @@ Though it's 2016 already, the support for C++11 is still a bit sparse. Currently
- GCC 4.9 - 6.0 (and possibly later) - GCC 4.9 - 6.0 (and possibly later)
- Clang 3.4 - 3.9 (and possibly later) - Clang 3.4 - 3.9 (and possibly later)
- Microsoft Visual C++ 2015 / 14.0 (and possibly later) - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
I would be happy to learn about other compilers/versions. I would be happy to learn about other compilers/versions.
@ -483,6 +483,9 @@ I deeply appreciate the help of the following people.
- [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake. - [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake.
- [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files. - [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files.
- [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal). - [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! Thanks a lot for helping out!
@ -502,7 +505,7 @@ $ make
$ ./json_unit "*" $ ./json_unit "*"
=============================================================================== ===============================================================================
All tests passed (5568715 assertions in 32 test cases) All tests passed (5568718 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). For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).

View file

@ -1,4 +1,5 @@
SRCDIR = ../src SRCDIR = ../src
SED:=$(shell command -v gsed || which sed)
all: doxygen all: doxygen
@ -52,10 +53,10 @@ clean:
# create Doxygen documentation # create Doxygen documentation
doxygen: create_output create_links doxygen: create_output create_links
doxygen doxygen
gsed -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType &gt;@@g' html/*.html $(SED) -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;&#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 $(SED) -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@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
upload: clean doxygen check_output upload: clean doxygen check_output
cd html ; ../scripts/git-update-ghpages nlohmann/json cd html ; ../scripts/git-update-ghpages nlohmann/json
@ -70,14 +71,14 @@ upload: clean doxygen check_output
# create docset for Dash # create docset for Dash
docset: create_output docset: create_output
cp Doxyfile Doxyfile_docset cp Doxyfile Doxyfile_docset
gsed -i 's/DISABLE_INDEX = NO/DISABLE_INDEX = YES/' Doxyfile_docset $(SED) -i 's/DISABLE_INDEX = NO/DISABLE_INDEX = YES/' Doxyfile_docset
gsed -i 's/SEARCHENGINE = YES/SEARCHENGINE = NO/' Doxyfile_docset $(SED) -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@HTML_EXTRA_STYLESHEET = css/mylayout.css@HTML_EXTRA_STYLESHEET = css/mylayout_docset.css@' Doxyfile_docset
rm -fr html *.docset rm -fr html *.docset
doxygen Doxyfile_docset doxygen Doxyfile_docset
gsed -i 's@&lt; ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType &gt;@@g' html/*.html $(SED) -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;&#160;ObjectType,&#160;ArrayType,&#160;StringType,&#160;BooleanType,&#160;NumberIntegerType,&#160;NumberFloatType,&#160;AllocatorType&#160;&gt;@@g' html/*.html
make -C html make -C html
mv html/*.docset . 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 rm -fr Doxyfile_docset html

View file

@ -12,7 +12,6 @@ int main()
auto count_three = j_object.count("three"); auto count_three = j_object.count("three");
// print values // print values
std::cout << std::boolalpha;
std::cout << "number of elements with key \"two\": " << count_two << '\n'; std::cout << "number of elements with key \"two\": " << count_two << '\n';
std::cout << "number of elements with key \"three\": " << count_three << '\n'; std::cout << "number of elements with key \"three\": " << count_three << '\n';
} }

View file

@ -1 +1 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/XoXqF9LlWu8jlNgE"><b>online</b></a> <a target="_blank" href="http://melpon.org/wandbox/permlink/yv5TMrq9qREivvHf"><b>online</b></a>

View file

@ -1,7 +1,7 @@
/* /*
__ _____ _____ _____ __ _____ _____ _____
__| | __| | | | JSON for Modern C++ __| | __| | | | JSON for Modern C++
| | |__ | | | | | | version 2.0.0 | | |__ | | | | | | version 2.0.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json |_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>. Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@ -36,7 +36,7 @@ SOFTWARE.
#include <ciso646> #include <ciso646>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
@ -46,6 +46,7 @@ SOFTWARE.
#include <limits> #include <limits>
#include <map> #include <map>
#include <memory> #include <memory>
#include <numeric>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
@ -53,6 +54,19 @@ SOFTWARE.
#include <utility> #include <utility>
#include <vector> #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 // disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
@ -76,7 +90,13 @@ namespace
{ {
/*! /*!
@brief Helper to determine whether there's a key_type for T. @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 @sa http://stackoverflow.com/a/7728728/266378
@since version 1.0.0
*/ */
template<typename T> template<typename T>
struct has_mapped_type struct has_mapped_type
@ -90,11 +110,18 @@ struct has_mapped_type
/*! /*!
@brief helper class to create locales with decimal point @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 @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 char do_decimal_point() const
{ {
return '.'; return '.';
@ -188,13 +215,8 @@ class basic_json
{ {
private: private:
/// workaround type for MSVC /// workaround type for MSVC
using basic_json_t = basic_json<ObjectType, using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
ArrayType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
StringType,
BooleanType,
NumberIntegerType,
NumberUnsignedType,
NumberFloatType,
AllocatorType>; AllocatorType>;
public: public:
@ -207,6 +229,8 @@ class basic_json
///////////////////// /////////////////////
/// @name container types /// @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 /// the type of elements in a basic_json container
@ -256,6 +280,8 @@ class basic_json
/////////////////////////// ///////////////////////////
/// @name JSON value data types /// @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.
/// @{ /// @{
/*! /*!
@ -923,6 +949,8 @@ class basic_json
////////////////// //////////////////
/// @name constructors and destructors /// @name constructors and destructors
/// Constructors of class @ref basic_json, copy/move constructor, copy
/// assignment, static functions creating objects, and the destructor.
/// @{ /// @{
/*! /*!
@ -1554,22 +1582,13 @@ class basic_json
bool type_deduction = true, bool type_deduction = true,
value_t manual_type = value_t::array) 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 // check if each element is an array with two elements whose first
// element is a string // 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 return element.is_array() and element.size() == 2 and element[0].is_string();
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;
}
}
// adjust type if type deduction is not wanted // adjust type if type deduction is not wanted
if (not type_deduction) if (not type_deduction)
@ -1595,10 +1614,10 @@ class basic_json
assert(m_value.object != nullptr); 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]); m_value.object->emplace(*(element[0].m_value.string), element[1]);
} });
} }
else else
{ {
@ -2069,19 +2088,20 @@ class basic_json
/////////////////////// ///////////////////////
/// @name object inspection /// @name object inspection
/// Functions to inspect the type of a JSON value.
/// @{ /// @{
/*! /*!
@brief serialization @brief serialization
Serialization function for JSON values. The function tries to mimic 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. 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 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 `0` will only insert newlines. `-1` (the default) selects the most compact
representation representation.
@return string containing the serialization of the JSON value @return string containing the serialization of the JSON value
@ -2097,6 +2117,8 @@ class basic_json
string_t dump(const int indent = -1) const string_t dump(const int indent = -1) const
{ {
std::stringstream ss; std::stringstream ss;
// fix locale problems
ss.imbue(std::locale(std::locale(), new DecimalSeparator));
if (indent >= 0) if (indent >= 0)
{ {
@ -2765,6 +2787,7 @@ class basic_json
public: public:
/// @name value access /// @name value access
/// Direct access to the stored value of a JSON value.
/// @{ /// @{
/*! /*!
@ -2815,7 +2838,8 @@ class basic_json
Explicit pointer access to the internally stored JSON value. No copies are Explicit pointer access to the internally stored JSON value. No copies are
made. 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 @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, object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ -2870,7 +2894,8 @@ class basic_json
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref @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, 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 @return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@ -2890,6 +2915,21 @@ class basic_json
, int>::type = 0> , int>::type = 0>
PointerType get_ptr() noexcept 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<>() // delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr)); return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
@ -2905,6 +2945,21 @@ class basic_json
, int>::type = 0> , int>::type = 0>
constexpr const PointerType get_ptr() const noexcept 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 // delegate the call to get_impl_ptr<>() const
return get_impl_ptr(static_cast<const PointerType>(nullptr)); return get_impl_ptr(static_cast<const PointerType>(nullptr));
} }
@ -2920,7 +2975,7 @@ class basic_json
@tparam ReferenceType reference type; must be a reference to @ref array_t, @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 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 @return reference to the internally stored JSON value if the requested
reference type @a ReferenceType fits to the JSON value; throws reference type @a ReferenceType fits to the JSON value; throws
@ -3010,6 +3065,7 @@ class basic_json
//////////////////// ////////////////////
/// @name element access /// @name element access
/// Access to the JSON value.
/// @{ /// @{
/*! /*!
@ -3233,11 +3289,13 @@ class basic_json
// operator[] only works for arrays // operator[] only works for arrays
if (is_array()) if (is_array())
{ {
// fill up array with null values until given idx is reached // fill up array with null values if given idx is outside range
assert(m_value.array != nullptr); assert(m_value.array != nullptr);
for (size_t i = m_value.array->size(); i <= idx; ++i) 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); return m_value.array->operator[](idx);
@ -5655,9 +5713,14 @@ class basic_json
// reset width to 0 for subsequent calls to this stream // reset width to 0 for subsequent calls to this stream
o.width(0); o.width(0);
// fix locale problems
auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
// do the actual serialization // do the actual serialization
j.dump(o, pretty_print, static_cast<unsigned int>(indentation)); j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
// reset locale
o.imbue(old_locale);
return o; return o;
} }
@ -5794,7 +5857,7 @@ class basic_json
/////////////////////////// ///////////////////////////
/// return the type as string /// return the type as string
string_t type_name() const noexcept std::string type_name() const
{ {
switch (m_type) switch (m_type)
{ {
@ -5825,9 +5888,8 @@ class basic_json
*/ */
static std::size_t extra_space(const string_t& s) noexcept static std::size_t extra_space(const string_t& s) noexcept
{ {
std::size_t result = 0; return std::accumulate(s.begin(), s.end(), size_t{},
[](size_t res, typename string_t::value_type c)
for (const auto& c : s)
{ {
switch (c) switch (c)
{ {
@ -5840,8 +5902,7 @@ class basic_json
case '\t': case '\t':
{ {
// from c (1 byte) to \x (2 bytes) // from c (1 byte) to \x (2 bytes)
result += 1; return res + 1;
break;
} }
default: default:
@ -5849,14 +5910,15 @@ class basic_json
if (c >= 0x00 and c <= 0x1f) if (c >= 0x00 and c <= 0x1f)
{ {
// from c (1 byte) to \uxxxx (6 bytes) // from c (1 byte) to \uxxxx (6 bytes)
result += 5; return res + 5;
} }
break; else
{
return res;
} }
} }
} }
});
return result;
} }
/*! /*!
@ -5950,16 +6012,15 @@ class basic_json
{ {
// convert a number 0..15 to its hex representation // convert a number 0..15 to its hex representation
// (0..f) // (0..f)
const auto hexify = [](const int v) -> char static const char hexify[16] =
{ {
return (v < 10) '0', '1', '2', '3', '4', '5', '6', '7',
? ('0' + static_cast<char>(v)) '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
: ('a' + static_cast<char>((v - 10) & 0x1f));
}; };
// print character c as \uxxxx // print character c as \uxxxx
for (const char m : 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; result[++pos] = m;
@ -6128,11 +6189,8 @@ class basic_json
// string->double->string or string->long // string->double->string or string->long
// double->string; to be safe, we read this value from // double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10 // std::numeric_limits<number_float_t>::digits10
std::stringstream ss; o << std::setprecision(std::numeric_limits<double>::digits10)
ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
ss << std::setprecision(std::numeric_limits<double>::digits10)
<< m_value.number_float; << m_value.number_float;
o << ss.str();
} }
return; return;
} }
@ -6609,13 +6667,13 @@ class basic_json
{ {
case basic_json::value_t::object: case basic_json::value_t::object:
{ {
++m_it.object_iterator; std::advance(m_it.object_iterator, 1);
break; break;
} }
case basic_json::value_t::array: case basic_json::value_t::array:
{ {
++m_it.array_iterator; std::advance(m_it.array_iterator, 1);
break; break;
} }
@ -6646,13 +6704,13 @@ class basic_json
{ {
case basic_json::value_t::object: case basic_json::value_t::object:
{ {
--m_it.object_iterator; std::advance(m_it.object_iterator, -1);
break; break;
} }
case basic_json::value_t::array: case basic_json::value_t::array:
{ {
--m_it.array_iterator; std::advance(m_it.array_iterator, -1);
break; break;
} }
@ -6764,7 +6822,7 @@ class basic_json
case basic_json::value_t::array: case basic_json::value_t::array:
{ {
m_it.array_iterator += i; std::advance(m_it.array_iterator, i);
break; break;
} }
@ -6838,7 +6896,7 @@ class basic_json
case basic_json::value_t::array: 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: case basic_json::value_t::null:
@ -7333,6 +7391,8 @@ class basic_json
@return the class of the next token read from the buffer @return the class of the next token read from the buffer
*/ */
token_type scan() noexcept token_type scan() noexcept
{
while (true)
{ {
// pointer for backtracking information // pointer for backtracking information
m_marker = nullptr; m_marker = nullptr;
@ -7502,13 +7562,15 @@ class basic_json
basic_json_parser_2: basic_json_parser_2:
++m_cursor; ++m_cursor;
{ {
return token_type::end_of_input; last_token_type = token_type::end_of_input;
break;
} }
basic_json_parser_4: basic_json_parser_4:
++m_cursor; ++m_cursor;
basic_json_parser_5: basic_json_parser_5:
{ {
return token_type::parse_error; last_token_type = token_type::parse_error;
break;
} }
basic_json_parser_6: basic_json_parser_6:
++m_cursor; ++m_cursor;
@ -7522,7 +7584,7 @@ basic_json_parser_6:
goto basic_json_parser_6; goto basic_json_parser_6;
} }
{ {
return scan(); continue;
} }
basic_json_parser_9: basic_json_parser_9:
yyaccept = 0; yyaccept = 0;
@ -7535,7 +7597,8 @@ basic_json_parser_9:
basic_json_parser_10: basic_json_parser_10:
++m_cursor; ++m_cursor;
{ {
return token_type::value_separator; last_token_type = token_type::value_separator;
break;
} }
basic_json_parser_12: basic_json_parser_12:
yych = *++m_cursor; yych = *++m_cursor;
@ -7575,7 +7638,8 @@ basic_json_parser_13:
} }
basic_json_parser_14: basic_json_parser_14:
{ {
return token_type::value_number; last_token_type = token_type::value_number;
break;
} }
basic_json_parser_15: basic_json_parser_15:
yyaccept = 1; yyaccept = 1;
@ -7612,17 +7676,20 @@ basic_json_parser_15:
basic_json_parser_17: basic_json_parser_17:
++m_cursor; ++m_cursor;
{ {
return token_type::name_separator; last_token_type = token_type::name_separator;
break;
} }
basic_json_parser_19: basic_json_parser_19:
++m_cursor; ++m_cursor;
{ {
return token_type::begin_array; last_token_type = token_type::begin_array;
break;
} }
basic_json_parser_21: basic_json_parser_21:
++m_cursor; ++m_cursor;
{ {
return token_type::end_array; last_token_type = token_type::end_array;
break;
} }
basic_json_parser_23: basic_json_parser_23:
yyaccept = 0; yyaccept = 0;
@ -7651,12 +7718,14 @@ basic_json_parser_25:
basic_json_parser_26: basic_json_parser_26:
++m_cursor; ++m_cursor;
{ {
return token_type::begin_object; last_token_type = token_type::begin_object;
break;
} }
basic_json_parser_28: basic_json_parser_28:
++m_cursor; ++m_cursor;
{ {
return token_type::end_object; last_token_type = token_type::end_object;
break;
} }
basic_json_parser_30: basic_json_parser_30:
yyaccept = 0; yyaccept = 0;
@ -7700,7 +7769,8 @@ basic_json_parser_33:
basic_json_parser_34: basic_json_parser_34:
++m_cursor; ++m_cursor;
{ {
return token_type::value_string; last_token_type = token_type::value_string;
break;
} }
basic_json_parser_36: basic_json_parser_36:
++m_cursor; ++m_cursor;
@ -7963,7 +8033,7 @@ basic_json_parser_51:
basic_json_parser_52: basic_json_parser_52:
++m_cursor; ++m_cursor;
{ {
return scan(); continue;
} }
basic_json_parser_54: basic_json_parser_54:
++m_cursor; ++m_cursor;
@ -8010,12 +8080,14 @@ basic_json_parser_55:
basic_json_parser_56: basic_json_parser_56:
++m_cursor; ++m_cursor;
{ {
return token_type::literal_null; last_token_type = token_type::literal_null;
break;
} }
basic_json_parser_58: basic_json_parser_58:
++m_cursor; ++m_cursor;
{ {
return token_type::literal_true; last_token_type = token_type::literal_true;
break;
} }
basic_json_parser_60: basic_json_parser_60:
++m_cursor; ++m_cursor;
@ -8055,7 +8127,8 @@ basic_json_parser_60:
basic_json_parser_61: basic_json_parser_61:
++m_cursor; ++m_cursor;
{ {
return token_type::literal_false; last_token_type = token_type::literal_false;
break;
} }
basic_json_parser_63: basic_json_parser_63:
++m_cursor; ++m_cursor;
@ -8096,6 +8169,9 @@ basic_json_parser_63:
} }
return last_token_type;
}
/// append data from the stream to the internal buffer /// append data from the stream to the internal buffer
void yyfill() noexcept void yyfill() noexcept
{ {
@ -8123,7 +8199,7 @@ basic_json_parser_63:
} }
/// return string representation of last read token /// return string representation of last read token
string_t get_token() const string_t get_token_string() const
{ {
assert(m_start != nullptr); assert(m_start != nullptr);
return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start),
@ -8391,7 +8467,7 @@ basic_json_parser_63:
if (type != value_t::number_float) if (type != value_t::number_float)
{ {
// multiply last value by ten and add the new digit // 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 // test for overflow
if (temp < value || temp > max) if (temp < value || temp > max)
@ -8441,6 +8517,8 @@ basic_json_parser_63:
const lexer_char_t* m_cursor = nullptr; const lexer_char_t* m_cursor = nullptr;
/// pointer to the end of the buffer /// pointer to the end of the buffer
const lexer_char_t* m_limit = nullptr; const lexer_char_t* m_limit = nullptr;
/// the last token type
token_type last_token_type = token_type::end_of_input;
}; };
/*! /*!
@ -8476,7 +8554,7 @@ basic_json_parser_63:
// return parser result and replace it with null in case the // return parser result and replace it with null in case the
// top-level value was discarded by the callback function // 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: private:
@ -8684,7 +8762,8 @@ basic_json_parser_63:
if (t != last_token) if (t != last_token)
{ {
std::string error_msg = "parse error - unexpected "; 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)); lexer::token_type_name(last_token));
error_msg += "; expected " + lexer::token_type_name(t); error_msg += "; expected " + lexer::token_type_name(t);
throw std::invalid_argument(error_msg); throw std::invalid_argument(error_msg);
@ -8696,7 +8775,8 @@ basic_json_parser_63:
if (t == last_token) if (t == last_token)
{ {
std::string error_msg = "parse error - unexpected "; 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)); lexer::token_type_name(last_token));
throw std::invalid_argument(error_msg); throw std::invalid_argument(error_msg);
} }
@ -8774,14 +8854,12 @@ basic_json_parser_63:
*/ */
std::string to_string() const noexcept std::string to_string() const noexcept
{ {
std::string result; return std::accumulate(reference_tokens.begin(),
reference_tokens.end(), std::string{},
for (const auto& reference_token : reference_tokens) [](const std::string & a, const std::string & b)
{ {
result += "/" + escape(reference_token); return a + "/" + escape(b);
} });
return result;
} }
/// @copydoc to_string() /// @copydoc to_string()

View file

@ -1,7 +1,7 @@
/* /*
__ _____ _____ _____ __ _____ _____ _____
__| | __| | | | JSON for Modern C++ __| | __| | | | JSON for Modern C++
| | |__ | | | | | | version 2.0.0 | | |__ | | | | | | version 2.0.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json |_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>. Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@ -36,7 +36,7 @@ SOFTWARE.
#include <ciso646> #include <ciso646>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
@ -46,6 +46,7 @@ SOFTWARE.
#include <limits> #include <limits>
#include <map> #include <map>
#include <memory> #include <memory>
#include <numeric>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
@ -53,6 +54,19 @@ SOFTWARE.
#include <utility> #include <utility>
#include <vector> #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 // disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
@ -76,7 +90,13 @@ namespace
{ {
/*! /*!
@brief Helper to determine whether there's a key_type for T. @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 @sa http://stackoverflow.com/a/7728728/266378
@since version 1.0.0
*/ */
template<typename T> template<typename T>
struct has_mapped_type struct has_mapped_type
@ -90,11 +110,18 @@ struct has_mapped_type
/*! /*!
@brief helper class to create locales with decimal point @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 @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 char do_decimal_point() const
{ {
return '.'; return '.';
@ -188,13 +215,8 @@ class basic_json
{ {
private: private:
/// workaround type for MSVC /// workaround type for MSVC
using basic_json_t = basic_json<ObjectType, using basic_json_t = basic_json<ObjectType, ArrayType, StringType,
ArrayType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType,
StringType,
BooleanType,
NumberIntegerType,
NumberUnsignedType,
NumberFloatType,
AllocatorType>; AllocatorType>;
public: public:
@ -207,6 +229,8 @@ class basic_json
///////////////////// /////////////////////
/// @name container types /// @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 /// the type of elements in a basic_json container
@ -256,6 +280,8 @@ class basic_json
/////////////////////////// ///////////////////////////
/// @name JSON value data types /// @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.
/// @{ /// @{
/*! /*!
@ -923,6 +949,8 @@ class basic_json
////////////////// //////////////////
/// @name constructors and destructors /// @name constructors and destructors
/// Constructors of class @ref basic_json, copy/move constructor, copy
/// assignment, static functions creating objects, and the destructor.
/// @{ /// @{
/*! /*!
@ -1554,22 +1582,13 @@ class basic_json
bool type_deduction = true, bool type_deduction = true,
value_t manual_type = value_t::array) 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 // check if each element is an array with two elements whose first
// element is a string // 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 return element.is_array() and element.size() == 2 and element[0].is_string();
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;
}
}
// adjust type if type deduction is not wanted // adjust type if type deduction is not wanted
if (not type_deduction) if (not type_deduction)
@ -1595,10 +1614,10 @@ class basic_json
assert(m_value.object != nullptr); 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]); m_value.object->emplace(*(element[0].m_value.string), element[1]);
} });
} }
else else
{ {
@ -2069,19 +2088,20 @@ class basic_json
/////////////////////// ///////////////////////
/// @name object inspection /// @name object inspection
/// Functions to inspect the type of a JSON value.
/// @{ /// @{
/*! /*!
@brief serialization @brief serialization
Serialization function for JSON values. The function tries to mimic 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. 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 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 `0` will only insert newlines. `-1` (the default) selects the most compact
representation representation.
@return string containing the serialization of the JSON value @return string containing the serialization of the JSON value
@ -2097,6 +2117,8 @@ class basic_json
string_t dump(const int indent = -1) const string_t dump(const int indent = -1) const
{ {
std::stringstream ss; std::stringstream ss;
// fix locale problems
ss.imbue(std::locale(std::locale(), new DecimalSeparator));
if (indent >= 0) if (indent >= 0)
{ {
@ -2765,6 +2787,7 @@ class basic_json
public: public:
/// @name value access /// @name value access
/// Direct access to the stored value of a JSON value.
/// @{ /// @{
/*! /*!
@ -2815,7 +2838,8 @@ class basic_json
Explicit pointer access to the internally stored JSON value. No copies are Explicit pointer access to the internally stored JSON value. No copies are
made. 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 @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, object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
@ -2870,7 +2894,8 @@ class basic_json
@tparam PointerType pointer type; must be a pointer to @ref array_t, @ref @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, 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 @return pointer to the internally stored JSON value if the requested
pointer type @a PointerType fits to the JSON value; `nullptr` otherwise pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
@ -2890,6 +2915,21 @@ class basic_json
, int>::type = 0> , int>::type = 0>
PointerType get_ptr() noexcept 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<>() // delegate the call to get_impl_ptr<>()
return get_impl_ptr(static_cast<PointerType>(nullptr)); return get_impl_ptr(static_cast<PointerType>(nullptr));
} }
@ -2905,6 +2945,21 @@ class basic_json
, int>::type = 0> , int>::type = 0>
constexpr const PointerType get_ptr() const noexcept 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 // delegate the call to get_impl_ptr<>() const
return get_impl_ptr(static_cast<const PointerType>(nullptr)); return get_impl_ptr(static_cast<const PointerType>(nullptr));
} }
@ -2920,7 +2975,7 @@ class basic_json
@tparam ReferenceType reference type; must be a reference to @ref array_t, @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 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 @return reference to the internally stored JSON value if the requested
reference type @a ReferenceType fits to the JSON value; throws reference type @a ReferenceType fits to the JSON value; throws
@ -3010,6 +3065,7 @@ class basic_json
//////////////////// ////////////////////
/// @name element access /// @name element access
/// Access to the JSON value.
/// @{ /// @{
/*! /*!
@ -3233,11 +3289,13 @@ class basic_json
// operator[] only works for arrays // operator[] only works for arrays
if (is_array()) if (is_array())
{ {
// fill up array with null values until given idx is reached // fill up array with null values if given idx is outside range
assert(m_value.array != nullptr); assert(m_value.array != nullptr);
for (size_t i = m_value.array->size(); i <= idx; ++i) 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); return m_value.array->operator[](idx);
@ -5655,9 +5713,14 @@ class basic_json
// reset width to 0 for subsequent calls to this stream // reset width to 0 for subsequent calls to this stream
o.width(0); o.width(0);
// fix locale problems
auto old_locale = o.imbue(std::locale(std::locale(), new DecimalSeparator));
// do the actual serialization // do the actual serialization
j.dump(o, pretty_print, static_cast<unsigned int>(indentation)); j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
// reset locale
o.imbue(old_locale);
return o; return o;
} }
@ -5794,7 +5857,7 @@ class basic_json
/////////////////////////// ///////////////////////////
/// return the type as string /// return the type as string
string_t type_name() const noexcept std::string type_name() const
{ {
switch (m_type) switch (m_type)
{ {
@ -5825,9 +5888,8 @@ class basic_json
*/ */
static std::size_t extra_space(const string_t& s) noexcept static std::size_t extra_space(const string_t& s) noexcept
{ {
std::size_t result = 0; return std::accumulate(s.begin(), s.end(), size_t{},
[](size_t res, typename string_t::value_type c)
for (const auto& c : s)
{ {
switch (c) switch (c)
{ {
@ -5840,8 +5902,7 @@ class basic_json
case '\t': case '\t':
{ {
// from c (1 byte) to \x (2 bytes) // from c (1 byte) to \x (2 bytes)
result += 1; return res + 1;
break;
} }
default: default:
@ -5849,14 +5910,15 @@ class basic_json
if (c >= 0x00 and c <= 0x1f) if (c >= 0x00 and c <= 0x1f)
{ {
// from c (1 byte) to \uxxxx (6 bytes) // from c (1 byte) to \uxxxx (6 bytes)
result += 5; return res + 5;
} }
break; else
{
return res;
} }
} }
} }
});
return result;
} }
/*! /*!
@ -5950,16 +6012,15 @@ class basic_json
{ {
// convert a number 0..15 to its hex representation // convert a number 0..15 to its hex representation
// (0..f) // (0..f)
const auto hexify = [](const int v) -> char static const char hexify[16] =
{ {
return (v < 10) '0', '1', '2', '3', '4', '5', '6', '7',
? ('0' + static_cast<char>(v)) '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
: ('a' + static_cast<char>((v - 10) & 0x1f));
}; };
// print character c as \uxxxx // print character c as \uxxxx
for (const char m : 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; result[++pos] = m;
@ -6128,11 +6189,8 @@ class basic_json
// string->double->string or string->long // string->double->string or string->long
// double->string; to be safe, we read this value from // double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10 // std::numeric_limits<number_float_t>::digits10
std::stringstream ss; o << std::setprecision(std::numeric_limits<double>::digits10)
ss.imbue(std::locale(std::locale(), new DecimalSeparator)); // fix locale problems
ss << std::setprecision(std::numeric_limits<double>::digits10)
<< m_value.number_float; << m_value.number_float;
o << ss.str();
} }
return; return;
} }
@ -6609,13 +6667,13 @@ class basic_json
{ {
case basic_json::value_t::object: case basic_json::value_t::object:
{ {
++m_it.object_iterator; std::advance(m_it.object_iterator, 1);
break; break;
} }
case basic_json::value_t::array: case basic_json::value_t::array:
{ {
++m_it.array_iterator; std::advance(m_it.array_iterator, 1);
break; break;
} }
@ -6646,13 +6704,13 @@ class basic_json
{ {
case basic_json::value_t::object: case basic_json::value_t::object:
{ {
--m_it.object_iterator; std::advance(m_it.object_iterator, -1);
break; break;
} }
case basic_json::value_t::array: case basic_json::value_t::array:
{ {
--m_it.array_iterator; std::advance(m_it.array_iterator, -1);
break; break;
} }
@ -6764,7 +6822,7 @@ class basic_json
case basic_json::value_t::array: case basic_json::value_t::array:
{ {
m_it.array_iterator += i; std::advance(m_it.array_iterator, i);
break; break;
} }
@ -6838,7 +6896,7 @@ class basic_json
case basic_json::value_t::array: 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: case basic_json::value_t::null:
@ -7333,6 +7391,8 @@ class basic_json
@return the class of the next token read from the buffer @return the class of the next token read from the buffer
*/ */
token_type scan() noexcept token_type scan() noexcept
{
while (true)
{ {
// pointer for backtracking information // pointer for backtracking information
m_marker = nullptr; m_marker = nullptr;
@ -7354,24 +7414,24 @@ class basic_json
// ignore whitespace // ignore whitespace
ws = [ \t\n\r]+; ws = [ \t\n\r]+;
ws { return scan(); } ws { continue; }
// ignore byte-order-mark // ignore byte-order-mark
bom = "\xEF\xBB\xBF"; bom = "\xEF\xBB\xBF";
bom { return scan(); } bom { continue; }
// structural characters // structural characters
"[" { return token_type::begin_array; } "[" { last_token_type = token_type::begin_array; break; }
"]" { return token_type::end_array; } "]" { last_token_type = token_type::end_array; break; }
"{" { return token_type::begin_object; } "{" { last_token_type = token_type::begin_object; break; }
"}" { return token_type::end_object; } "}" { last_token_type = token_type::end_object; break; }
"," { return token_type::value_separator; } "," { last_token_type = token_type::value_separator; break; }
":" { return token_type::name_separator; } ":" { last_token_type = token_type::name_separator; break; }
// literal names // literal names
"null" { return token_type::literal_null; } "null" { last_token_type = token_type::literal_null; break; }
"true" { return token_type::literal_true; } "true" { last_token_type = token_type::literal_true; break; }
"false" { return token_type::literal_false; } "false" { last_token_type = token_type::literal_false; break; }
// number // number
decimal_point = [.]; decimal_point = [.];
@ -7385,7 +7445,7 @@ class basic_json
frac = decimal_point digit+; frac = decimal_point digit+;
int = (zero|digit_1_9 digit*); int = (zero|digit_1_9 digit*);
number = minus? int frac? exp?; number = minus? int frac? exp?;
number { return token_type::value_number; } number { last_token_type = token_type::value_number; break; }
// string // string
quotation_mark = ["]; quotation_mark = ["];
@ -7396,16 +7456,19 @@ class basic_json
escaped = escape (single_escaped | unicode_escaped); escaped = escape (single_escaped | unicode_escaped);
char = unescaped | escaped; char = unescaped | escaped;
string = quotation_mark char* quotation_mark; string = quotation_mark char* quotation_mark;
string { return token_type::value_string; } string { last_token_type = token_type::value_string; break; }
// end of file // end of file
'\000' { return token_type::end_of_input; } '\000' { last_token_type = token_type::end_of_input; break; }
// anything else is an error // anything else is an error
. { return token_type::parse_error; } . { last_token_type = token_type::parse_error; break; }
*/ */
} }
return last_token_type;
}
/// append data from the stream to the internal buffer /// append data from the stream to the internal buffer
void yyfill() noexcept void yyfill() noexcept
{ {
@ -7433,7 +7496,7 @@ class basic_json
} }
/// return string representation of last read token /// return string representation of last read token
string_t get_token() const string_t get_token_string() const
{ {
assert(m_start != nullptr); assert(m_start != nullptr);
return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start), return string_t(reinterpret_cast<typename string_t::const_pointer>(m_start),
@ -7701,7 +7764,7 @@ class basic_json
if (type != value_t::number_float) if (type != value_t::number_float)
{ {
// multiply last value by ten and add the new digit // 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 // test for overflow
if (temp < value || temp > max) if (temp < value || temp > max)
@ -7751,6 +7814,8 @@ class basic_json
const lexer_char_t* m_cursor = nullptr; const lexer_char_t* m_cursor = nullptr;
/// pointer to the end of the buffer /// pointer to the end of the buffer
const lexer_char_t* m_limit = nullptr; const lexer_char_t* m_limit = nullptr;
/// the last token type
token_type last_token_type = token_type::end_of_input;
}; };
/*! /*!
@ -7786,7 +7851,7 @@ class basic_json
// return parser result and replace it with null in case the // return parser result and replace it with null in case the
// top-level value was discarded by the callback function // 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: private:
@ -7994,7 +8059,8 @@ class basic_json
if (t != last_token) if (t != last_token)
{ {
std::string error_msg = "parse error - unexpected "; 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)); lexer::token_type_name(last_token));
error_msg += "; expected " + lexer::token_type_name(t); error_msg += "; expected " + lexer::token_type_name(t);
throw std::invalid_argument(error_msg); throw std::invalid_argument(error_msg);
@ -8006,7 +8072,8 @@ class basic_json
if (t == last_token) if (t == last_token)
{ {
std::string error_msg = "parse error - unexpected "; 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)); lexer::token_type_name(last_token));
throw std::invalid_argument(error_msg); throw std::invalid_argument(error_msg);
} }
@ -8084,14 +8151,12 @@ class basic_json
*/ */
std::string to_string() const noexcept std::string to_string() const noexcept
{ {
std::string result; return std::accumulate(reference_tokens.begin(),
reference_tokens.end(), std::string{},
for (const auto& reference_token : reference_tokens) [](const std::string & a, const std::string & b)
{ {
result += "/" + escape(reference_token); return a + "/" + escape(b);
} });
return result;
} }
/// @copydoc to_string() /// @copydoc to_string()

View file

@ -1,7 +1,7 @@
/* /*
__ _____ _____ _____ __ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite) __| | __| | | | JSON for Modern C++ (test suite)
| | |__ | | | | | | version 2.0.0 | | |__ | | | | | | version 2.0.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json |_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>. Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@ -10308,6 +10308,11 @@ TEST_CASE("parser class")
TEST_CASE("README", "[hide]") TEST_CASE("README", "[hide]")
{ {
{
// 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) // create an empty structure (null)
json j; json j;
@ -10531,7 +10536,7 @@ TEST_CASE("README", "[hide]")
})"_json; })"_json;
// access members with a JSON pointer (RFC 6901) // access members with a JSON pointer (RFC 6901)
j_original["/baz/2"_json_pointer]; j_original["/baz/1"_json_pointer];
// "two" // "two"
// a JSON patch (RFC 6902) // a JSON patch (RFC 6902)
@ -10556,6 +10561,10 @@ TEST_CASE("README", "[hide]")
// { "op":"add","path":"/foo","value":"bar" } // { "op":"add","path":"/foo","value":"bar" }
// ] // ]
} }
// restore old std::cout
std::cout.rdbuf(old_cout_buffer);
}
} }
TEST_CASE("algorithms") TEST_CASE("algorithms")
@ -14079,6 +14088,16 @@ TEST_CASE("regression tests")
CHECK(j1a.dump() == "23.42"); CHECK(j1a.dump() == "23.42");
CHECK(j1b.dump() == "23.42"); CHECK(j1b.dump() == "23.42");
// check if locale is properly reset
std::stringstream ss;
ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator));
ss << 47.11;
CHECK(ss.str() == "47,11");
ss << j1a;
CHECK(ss.str() == "47,1123.42");
ss << 47.11;
CHECK(ss.str() == "47,1123.4247,11");
CHECK(j2a.dump() == "23.42"); CHECK(j2a.dump() == "23.42");
//issue #230 //issue #230
//CHECK(j2b.dump() == "23.42"); //CHECK(j2b.dump() == "23.42");