Merge branch 'feature/iterator_range_parsing' into develop

pull/313/head
Niels 2016-08-31 20:23:24 +02:00
commit 776880bc49
18 changed files with 1075 additions and 498 deletions

View File

@ -41,16 +41,10 @@ There are currently two files which need to be edited:
2. [`test/src/unit.cpp`](https://github.com/nlohmann/json/blob/master/test/unit.cpp) - This contains the [Catch](https://github.com/philsquared/Catch) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code.
If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled with
If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled and executed with
```sh
make
```
and can be executed with
```sh
./json_unit
make check
```
The test cases are also executed with several different compilers on [Travis](https://travis-ci.org/nlohmann/json) once you open a pull request.

View File

@ -492,6 +492,8 @@ I deeply appreciate the help of the following people.
- [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.
- [Thomas Braun](https://github.com/t-b) fixed a warning in a test case.
- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290).
Thanks a lot for helping out!
@ -510,7 +512,7 @@ To compile and run the tests, you need to execute
$ make check
===============================================================================
All tests passed (8905099 assertions in 32 test cases)
All tests passed (8905154 assertions in 35 test cases)
```
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).

View File

@ -0,0 +1,28 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// a JSON text
char text[] = R"(
{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http://www.example.com/image/481989943",
"Height": 125,
"Width": 100
},
"Animated" : false,
"IDs": [116, 943, 234, 38793]
}
}
)";
// parse and serialize JSON
json j_complete = json::parse(text);
std::cout << std::setw(4) << j_complete << "\n\n";
}

View File

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

View File

@ -0,0 +1,20 @@
{
"Image": {
"Animated": false,
"Height": 600,
"IDs": [
116,
943,
234,
38793
],
"Thumbnail": {
"Height": 125,
"Url": "http://www.example.com/image/481989943",
"Width": 100
},
"Title": "View from 15th Floor",
"Width": 800
}
}

View File

@ -0,0 +1,13 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// a JSON text given as std::vector
std::vector<uint8_t> text = {'[', '1', ',', '2', ',', '3', ']', '\0'};
// parse and serialize JSON
json j_complete = json::parse(text);
std::cout << std::setw(4) << j_complete << "\n\n";
}

View File

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

View File

@ -0,0 +1,6 @@
[
1,
2,
3
]

View File

@ -0,0 +1,13 @@
#include <json.hpp>
using json = nlohmann::json;
int main()
{
// a JSON text given as std::vector
std::vector<uint8_t> text = {'[', '1', ',', '2', ',', '3', ']', '\0'};
// parse and serialize JSON
json j_complete = json::parse(text.begin(), text.end());
std::cout << std::setw(4) << j_complete << "\n\n";
}

View File

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

View File

@ -0,0 +1,6 @@
[
1,
2,
3
]

View File

@ -5,7 +5,7 @@ using json = nlohmann::json;
int main()
{
// a JSON text
std::string text = R"(
auto text = R"(
{
"Image": {
"Width": 800,
@ -44,4 +44,4 @@ int main()
// parse (with callback) and serialize JSON
json j_filtered = json::parse(text, cb);
std::cout << std::setw(4) << j_filtered << '\n';
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,7 @@ SOFTWARE.
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <initializer_list>
#include <iomanip>
@ -959,7 +960,7 @@ class basic_json
With a parser callback function, the result of parsing a JSON text can be
influenced. When passed to @ref parse(std::istream&, const
parser_callback_t) or @ref parse(const string_t&, const parser_callback_t),
parser_callback_t) or @ref parse(const char*, 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
@ -1002,7 +1003,7 @@ class basic_json
skipped completely or replaced by an empty discarded object.
@sa @ref parse(std::istream&, parser_callback_t) or
@ref parse(const string_t&, parser_callback_t) for examples
@ref parse(const char*, parser_callback_t) for examples
@since version 1.0.0
*/
@ -1140,11 +1141,9 @@ class basic_json
@since version 1.0.0
*/
template <class CompatibleObjectType, typename
std::enable_if<
std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type
= 0>
template<class CompatibleObjectType, typename std::enable_if<
std::is_constructible<typename object_t::key_type, typename CompatibleObjectType::key_type>::value and
std::is_constructible<basic_json, typename CompatibleObjectType::mapped_type>::value, int>::type = 0>
basic_json(const CompatibleObjectType& val)
: m_type(value_t::object)
{
@ -1205,16 +1204,14 @@ class basic_json
@since version 1.0.0
*/
template <class CompatibleArrayType, typename
std::enable_if<
not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type
= 0>
template<class CompatibleArrayType, typename std::enable_if<
not std::is_same<CompatibleArrayType, typename basic_json_t::iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::const_iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::reverse_iterator>::value and
not std::is_same<CompatibleArrayType, typename basic_json_t::const_reverse_iterator>::value and
not std::is_same<CompatibleArrayType, typename array_t::iterator>::value and
not std::is_same<CompatibleArrayType, typename array_t::const_iterator>::value and
std::is_constructible<basic_json, typename CompatibleArrayType::value_type>::value, int>::type = 0>
basic_json(const CompatibleArrayType& val)
: m_type(value_t::array)
{
@ -1300,10 +1297,8 @@ class basic_json
@since version 1.0.0
*/
template <class CompatibleStringType, typename
std::enable_if<
std::is_constructible<string_t, CompatibleStringType>::value, int>::type
= 0>
template<class CompatibleStringType, typename std::enable_if<
std::is_constructible<string_t, CompatibleStringType>::value, int>::type = 0>
basic_json(const CompatibleStringType& val)
: basic_json(string_t(val))
{
@ -1353,12 +1348,9 @@ class basic_json
@since version 1.0.0
*/
template<typename T,
typename std::enable_if<
not (std::is_same<T, int>::value)
and std::is_same<T, number_integer_t>::value
, int>::type
= 0>
template<typename T, typename std::enable_if<
not (std::is_same<T, int>::value) and
std::is_same<T, number_integer_t>::value, int>::type = 0>
basic_json(const number_integer_t val) noexcept
: m_type(value_t::number_integer), m_value(val)
{
@ -1422,13 +1414,11 @@ class basic_json
@since version 1.0.0
*/
template<typename CompatibleNumberIntegerType, typename
std::enable_if<
template<typename CompatibleNumberIntegerType, typename std::enable_if<
std::is_constructible<number_integer_t, CompatibleNumberIntegerType>::value and
std::numeric_limits<CompatibleNumberIntegerType>::is_integer and
std::numeric_limits<CompatibleNumberIntegerType>::is_signed,
CompatibleNumberIntegerType>::type
= 0>
CompatibleNumberIntegerType>::type = 0>
basic_json(const CompatibleNumberIntegerType val) noexcept
: m_type(value_t::number_integer),
m_value(static_cast<number_integer_t>(val))
@ -1453,12 +1443,9 @@ class basic_json
@since version 2.0.0
*/
template<typename T,
typename std::enable_if<
not (std::is_same<T, int>::value)
and std::is_same<T, number_unsigned_t>::value
, int>::type
= 0>
template<typename T, typename std::enable_if<
not (std::is_same<T, int>::value) and
std::is_same<T, number_unsigned_t>::value, int>::type = 0>
basic_json(const number_unsigned_t val) noexcept
: m_type(value_t::number_unsigned), m_value(val)
{
@ -1485,13 +1472,11 @@ class basic_json
@since version 2.0.0
*/
template <typename CompatibleNumberUnsignedType, typename
std::enable_if <
std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
CompatibleNumberUnsignedType>::type
= 0>
template<typename CompatibleNumberUnsignedType, typename std::enable_if <
std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
CompatibleNumberUnsignedType>::type = 0>
basic_json(const CompatibleNumberUnsignedType val) noexcept
: m_type(value_t::number_unsigned),
m_value(static_cast<number_unsigned_t>(val))
@ -1567,11 +1552,9 @@ class basic_json
@since version 1.0.0
*/
template<typename CompatibleNumberFloatType, typename = typename
std::enable_if<
template<typename CompatibleNumberFloatType, typename = typename std::enable_if<
std::is_constructible<number_float_t, CompatibleNumberFloatType>::value and
std::is_floating_point<CompatibleNumberFloatType>::value>::type
>
std::is_floating_point<CompatibleNumberFloatType>::value>::type>
basic_json(const CompatibleNumberFloatType val) noexcept
: basic_json(number_float_t(val))
{
@ -1838,12 +1821,9 @@ class basic_json
@since version 1.0.0
*/
template <class InputIT, typename
std::enable_if<
std::is_same<InputIT, typename basic_json_t::iterator>::value or
std::is_same<InputIT, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
template<class InputIT, typename std::enable_if<
std::is_same<InputIT, typename basic_json_t::iterator>::value or
std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>
basic_json(InputIT first, InputIT last)
{
assert(first.m_object != nullptr);
@ -2601,11 +2581,9 @@ class basic_json
//////////////////
/// get an object (explicit)
template <class T, typename
std::enable_if<
std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
std::is_convertible<basic_json_t, typename T::mapped_type>::value
, int>::type = 0>
template<class T, typename std::enable_if<
std::is_convertible<typename object_t::key_type, typename T::key_type>::value and
std::is_convertible<basic_json_t, typename T::mapped_type>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_object())
@ -2632,14 +2610,12 @@ class basic_json
}
/// get an array (explicit)
template <class T, typename
std::enable_if<
std::is_convertible<basic_json_t, typename T::value_type>::value and
not std::is_same<basic_json_t, typename T::value_type>::value and
not std::is_arithmetic<T>::value and
not std::is_convertible<std::string, T>::value and
not has_mapped_type<T>::value
, int>::type = 0>
template<class T, typename std::enable_if<
std::is_convertible<basic_json_t, typename T::value_type>::value and
not std::is_same<basic_json_t, typename T::value_type>::value and
not std::is_arithmetic<T>::value and
not std::is_convertible<std::string, T>::value and
not has_mapped_type<T>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_array())
@ -2659,11 +2635,9 @@ class basic_json
}
/// get an array (explicit)
template <class T, typename
std::enable_if<
std::is_convertible<basic_json_t, T>::value and
not std::is_same<basic_json_t, T>::value
, int>::type = 0>
template<class T, typename std::enable_if<
std::is_convertible<basic_json_t, T>::value and
not std::is_same<basic_json_t, T>::value, int>::type = 0>
std::vector<T> get_impl(std::vector<T>*) const
{
if (is_array())
@ -2684,11 +2658,9 @@ class basic_json
}
/// get an array (explicit)
template <class T, typename
std::enable_if<
std::is_same<basic_json, typename T::value_type>::value and
not has_mapped_type<T>::value
, int>::type = 0>
template<class T, typename std::enable_if<
std::is_same<basic_json, typename T::value_type>::value and
not has_mapped_type<T>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_array())
@ -2715,10 +2687,8 @@ class basic_json
}
/// get a string (explicit)
template <typename T, typename
std::enable_if<
std::is_convertible<string_t, T>::value
, int>::type = 0>
template<typename T, typename std::enable_if<
std::is_convertible<string_t, T>::value, int>::type = 0>
T get_impl(T*) const
{
if (is_string())
@ -2732,10 +2702,8 @@ class basic_json
}
/// get a number (explicit)
template<typename T, typename
std::enable_if<
std::is_arithmetic<T>::value
, int>::type = 0>
template<typename T, typename std::enable_if<
std::is_arithmetic<T>::value, int>::type = 0>
T get_impl(T*) const
{
switch (m_type)
@ -2924,10 +2892,8 @@ class basic_json
@since version 1.0.0
*/
template<typename ValueType, typename
std::enable_if<
not std::is_pointer<ValueType>::value
, int>::type = 0>
template<typename ValueType, typename std::enable_if<
not std::is_pointer<ValueType>::value, int>::type = 0>
ValueType get() const
{
return get_impl(static_cast<ValueType*>(nullptr));
@ -2960,10 +2926,8 @@ class basic_json
@since version 1.0.0
*/
template<typename PointerType, typename
std::enable_if<
std::is_pointer<PointerType>::value
, int>::type = 0>
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get() noexcept
{
// delegate the call to get_ptr
@ -2974,10 +2938,8 @@ class basic_json
@brief get a pointer value (explicit)
@copydoc get()
*/
template<typename PointerType, typename
std::enable_if<
std::is_pointer<PointerType>::value
, int>::type = 0>
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
constexpr const PointerType get() const noexcept
{
// delegate the call to get_ptr
@ -3010,10 +2972,8 @@ class basic_json
@since version 1.0.0
*/
template<typename PointerType, typename
std::enable_if<
std::is_pointer<PointerType>::value
, int>::type = 0>
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
PointerType get_ptr() noexcept
{
// get the type of the PointerType (remove pointer and const)
@ -3039,11 +2999,9 @@ class basic_json
@brief get a pointer value (implicit)
@copydoc get_ptr()
*/
template<typename PointerType, typename
std::enable_if<
std::is_pointer<PointerType>::value
and std::is_const<typename std::remove_pointer<PointerType>::type>::value
, int>::type = 0>
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value and
std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>
constexpr const PointerType get_ptr() const noexcept
{
// get the type of the PointerType (remove pointer and const)
@ -3091,10 +3049,8 @@ class basic_json
@since version 1.1.0
*/
template<typename ReferenceType, typename
std::enable_if<
std::is_reference<ReferenceType>::value
, int>::type = 0>
template<typename ReferenceType, typename std::enable_if<
std::is_reference<ReferenceType>::value, int>::type = 0>
ReferenceType get_ref()
{
// delegate call to get_ref_impl
@ -3105,11 +3061,9 @@ class basic_json
@brief get a reference value (implicit)
@copydoc get_ref()
*/
template<typename ReferenceType, typename
std::enable_if<
std::is_reference<ReferenceType>::value
and std::is_const<typename std::remove_reference<ReferenceType>::type>::value
, int>::type = 0>
template<typename ReferenceType, typename std::enable_if<
std::is_reference<ReferenceType>::value and
std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>
ReferenceType get_ref() const
{
// delegate call to get_ref_impl
@ -3144,10 +3098,9 @@ class basic_json
@since version 1.0.0
*/
template < typename ValueType, typename
std::enable_if <
not std::is_pointer<ValueType>::value
and not std::is_same<ValueType, typename string_t::value_type>::value
template < typename ValueType, typename std::enable_if <
not std::is_pointer<ValueType>::value and
not std::is_same<ValueType, typename string_t::value_type>::value
#ifndef _MSC_VER // Fix for issue #167 operator<< abiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif
@ -3737,10 +3690,8 @@ class basic_json
@since version 1.0.0
*/
template <class ValueType, typename
std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value
, int>::type = 0>
template<class ValueType, typename std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>
ValueType value(const typename object_t::key_type& key, ValueType default_value) const
{
// at only works for objects
@ -3813,10 +3764,8 @@ class basic_json
@since version 2.0.2
*/
template <class ValueType, typename
std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value
, int>::type = 0>
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
@ -3946,7 +3895,7 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a
pos refers to the last element, the `end()` iterator is returned.
@tparam InteratorType an @ref iterator or @ref const_iterator
@tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@ -3968,7 +3917,7 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType}
@sa @ref erase(InteratorType, InteratorType) -- removes the elements in
@sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@ -3977,13 +3926,11 @@ class basic_json
@since version 1.0.0
*/
template <class InteratorType, typename
std::enable_if<
std::is_same<InteratorType, typename basic_json_t::iterator>::value or
std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
InteratorType erase(InteratorType pos)
template<class IteratorType, typename std::enable_if<
std::is_same<IteratorType, typename basic_json_t::iterator>::value or
std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
= 0>
IteratorType erase(IteratorType pos)
{
// make sure iterator fits the current value
if (this != pos.m_object)
@ -3991,7 +3938,7 @@ class basic_json
throw std::domain_error("iterator does not fit current value");
}
InteratorType result = end();
IteratorType result = end();
switch (m_type)
{
@ -4055,7 +4002,7 @@ class basic_json
@return Iterator following the last removed element. If the iterator @a
second refers to the last element, the `end()` iterator is returned.
@tparam InteratorType an @ref iterator or @ref const_iterator
@tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@ -4078,7 +4025,7 @@ class basic_json
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType_IteratorType}
@sa @ref erase(InteratorType) -- removes the element at a given position
@sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@sa @ref erase(const size_type) -- removes the element from an array at
@ -4086,13 +4033,11 @@ class basic_json
@since version 1.0.0
*/
template <class InteratorType, typename
std::enable_if<
std::is_same<InteratorType, typename basic_json_t::iterator>::value or
std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
InteratorType erase(InteratorType first, InteratorType last)
template<class IteratorType, typename std::enable_if<
std::is_same<IteratorType, typename basic_json_t::iterator>::value or
std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type
= 0>
IteratorType erase(IteratorType first, IteratorType last)
{
// make sure iterator fits the current value
if (this != first.m_object or this != last.m_object)
@ -4100,7 +4045,7 @@ class basic_json
throw std::domain_error("iterators do not fit current value");
}
InteratorType result = end();
IteratorType result = end();
switch (m_type)
{
@ -4172,8 +4117,8 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__key_type}
@sa @ref erase(InteratorType) -- removes the element at a given position
@sa @ref erase(InteratorType, InteratorType) -- removes the elements in
@sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const size_type) -- removes the element from an array at
the given index
@ -4209,8 +4154,8 @@ class basic_json
@liveexample{The example shows the effect of `erase()`.,erase__size_type}
@sa @ref erase(InteratorType) -- removes the element at a given position
@sa @ref erase(InteratorType, InteratorType) -- removes the elements in
@sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@ -5918,10 +5863,16 @@ class basic_json
/// @{
/*!
@brief deserialize from string
@brief deserialize from an array
@param[in] s string to read a serialized JSON value from
@param[in] cb a parser callback function of type @ref parser_callback_t
This function reads from an array of 1-byte values.
@pre Each element of the container has a size of 1 byte. Violating this
precondition yields undefined behavior. **This precondition is enforced
with a static assertion.**
@param[in] array array to read from
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@ -5933,18 +5884,54 @@ class basic_json
@note A UTF-8 byte order mark is silently ignored.
@liveexample{The example below demonstrates the `parse()` function reading
from an array.,parse__array__parser_callback_t}
@since version 2.0.3
*/
template<class T, std::size_t N>
static basic_json parse(T (&array)[N],
const parser_callback_t cb = nullptr)
{
// delegate the call to the iterator-range parse overload
return parse(std::begin(array), std::end(array), cb);
}
/*!
@brief deserialize from string literal
@tparam CharT character/literal type with size of 1 byte
@param[in] s string literal to read a serialized JSON value from
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@return result of the deserialization
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@a cb has a super-linear complexity.
@note A UTF-8 byte order mark is silently ignored.
@note String containers like `std::string` or @ref string_t can be parsed
with @ref parse(const ContiguousContainer&, const parser_callback_t)
@liveexample{The example below demonstrates the `parse()` function with
and without callback function.,parse__string__parser_callback_t}
@sa @ref parse(std::istream&, const parser_callback_t) for a version that
reads from an input stream
@since version 1.0.0
@since version 1.0.0 (originally for @ref string_t)
*/
static basic_json parse(const string_t& s,
template<typename CharPT, typename std::enable_if<
std::is_pointer<CharPT>::value and
std::is_integral<typename std::remove_pointer<CharPT>::type>::value and
sizeof(typename std::remove_pointer<CharPT>::type) == 1, int>::type = 0>
static basic_json parse(const CharPT s,
const parser_callback_t cb = nullptr)
{
return parser(s, cb).parse();
return parser(reinterpret_cast<const char*>(s), cb).parse();
}
/*!
@ -5966,7 +5953,7 @@ 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&, const parser_callback_t) for a version
@sa @ref parse(const char*, const parser_callback_t) for a version
that reads from a string
@since version 1.0.0
@ -5986,6 +5973,130 @@ class basic_json
return parser(i, cb).parse();
}
/*!
@brief deserialize from an iterator range with contiguous storage
This function reads from an iterator range of a container with contiguous
storage of 1-byte values. Compatible container types include
`std::vector`, `std::string`, `std::array`, `std::valarray`, and
`std::initializer_list`. Furthermore, C-style arrays can be used with
`std::begin()`/`std::end()`. User-defined containers can be used as long
as they implement random-access iterators and a contiguous storage.
@pre The iterator range is contiguous. Violating this precondition yields
undefined behavior. **This precondition is enforced with an assertion.**
@pre Each element in the range has a size of 1 byte. Violating this
precondition yields undefined behavior. **This precondition is enforced
with a static assertion.**
@warning There is no way to enforce all preconditions at compile-time. If
the function is called with noncompliant iterators and with
assertions switched off, the behavior is undefined and will most
likely yield segmentation violation.
@tparam IteratorType iterator of container with contiguous storage
@param[in] first begin of the range to parse (included)
@param[in] last end of the range to parse (excluded)
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@return result of the deserialization
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@a cb has a super-linear complexity.
@note A UTF-8 byte order mark is silently ignored.
@liveexample{The example below demonstrates the `parse()` function reading
from an iterator range.,parse__iteratortype__parser_callback_t}
@since version 2.0.3
*/
template<class IteratorType, typename std::enable_if<
std::is_base_of<
std::random_access_iterator_tag,
typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
static basic_json parse(IteratorType first, IteratorType last,
const parser_callback_t cb = nullptr)
{
// assertion to check that the iterator range is indeed contiguous,
// see http://stackoverflow.com/a/35008842/266378 for more discussion
assert(std::accumulate(first, last, std::make_pair<bool, int>(true, 0),
[&first](std::pair<bool, int> res, decltype(*first) val)
{
res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
return res;
}).first);
// assertion to check that each element is 1 byte long
static_assert(sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
"each element in the iterator range must have the size of 1 byte");
// if iterator range is empty, create a parser with an empty string
// to generate "unexpected EOF" error message
if (std::distance(first, last) <= 0)
{
return parser("").parse();
}
return parser(first, last, cb).parse();
}
/*!
@brief deserialize from a container with contiguous storage
This function reads from a container with contiguous storage of 1-byte
values. Compatible container types include `std::vector`, `std::string`,
`std::array`, and `std::initializer_list`. User-defined containers can be
used as long as they implement random-access iterators and a contiguous
storage.
@pre The container storage is contiguous. Violating this precondition
yields undefined behavior. **This precondition is enforced with an
assertion.**
@pre Each element of the container has a size of 1 byte. Violating this
precondition yields undefined behavior. **This precondition is enforced
with a static assertion.**
@warning There is no way to enforce all preconditions at compile-time. If
the function is called with a noncompliant container and with
assertions switched off, the behavior is undefined and will most
likely yield segmentation violation.
@tparam ContiguousContainer container type with contiguous storage
@param[in] c container to read from
@param[in] cb a parser callback function of type @ref parser_callback_t
which is used to control the deserialization by filtering unwanted values
(optional)
@return result of the deserialization
@complexity Linear in the length of the input. The parser is a predictive
LL(1) parser. The complexity can be higher if the parser callback function
@a cb has a super-linear complexity.
@note A UTF-8 byte order mark is silently ignored.
@liveexample{The example below demonstrates the `parse()` function reading
from a contiguous container.,parse__contiguouscontainer__parser_callback_t}
@since version 2.0.3
*/
template<class ContiguousContainer, typename std::enable_if<
not std::is_pointer<ContiguousContainer>::value and
std::is_base_of<
std::random_access_iterator_tag,
typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value
, int>::type = 0>
static basic_json parse(const ContiguousContainer& c,
const parser_callback_t cb = nullptr)
{
// delegate the call to the iterator-range parse overload
return parse(std::begin(c), std::end(c), cb);
}
/*!
@brief deserialize from stream
@ -6039,7 +6150,7 @@ class basic_json
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
@return basically a string representation of a the @a m_type member
@complexity Constant.
@ -7490,32 +7601,25 @@ class basic_json
/// the char type to use in the lexer
using lexer_char_t = unsigned char;
/// constructor with a given buffer
explicit lexer(const string_t& s) noexcept
: m_stream(nullptr), m_buffer(s)
/// a lexer from a buffer with given length
lexer(const lexer_char_t* buff, const size_t len) noexcept
: m_content(buff)
{
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();
m_limit = m_content + len;
}
/// constructor with a given stream
explicit lexer(std::istream* s) noexcept
: m_stream(s), m_buffer()
/// a lexer from an input stream
explicit lexer(std::istream& s)
: m_stream(&s), m_line_buffer()
{
assert(m_stream != nullptr);
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;
m_limit = m_content + m_buffer.size();
// fill buffer
fill_line_buffer();
}
/// default constructor
lexer() = default;
// switch off unwanted functions
// switch off unwanted functions (due to pointer members)
lexer() = delete;
lexer(const lexer&) = delete;
lexer operator=(const lexer&) = delete;
@ -7668,7 +7772,7 @@ class basic_json
infinite sequence of whitespace or byte-order-marks. This contradicts
the assumption of finite input, q.e.d.
*/
token_type scan() noexcept
token_type scan()
{
while (true)
{
@ -7684,7 +7788,7 @@ class basic_json
re2c:define:YYCURSOR = m_cursor;
re2c:define:YYLIMIT = m_limit;
re2c:define:YYMARKER = m_marker;
re2c:define:YYFILL = "yyfill()";
re2c:define:YYFILL = "fill_line_buffer()";
re2c:yyfill:parameter = 0;
re2c:indent:string = " ";
re2c:indent:top = 1;
@ -7747,30 +7851,76 @@ class basic_json
return last_token_type;
}
/// append data from the stream to the internal buffer
void yyfill() noexcept
{
if (m_stream == nullptr or not * m_stream)
{
return;
}
/*!
@brief append data from the stream to the line buffer
This function is called by the scan() function when the end of the
buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
incremented without leaving the limits of the line buffer. Note re2c
decides when to call this function.
If the lexer reads from contiguous storage, there is no trailing null
byte. Therefore, this function must make sure to add these padding
null bytes.
If the lexer reads from an input stream, this function reads the next
line of the input.
@pre
p p p p p p u u u u u x . . . . . .
^ ^ ^ ^
m_content m_start | m_limit
m_cursor
@post
u u u u u x x x x x x x . . . . . .
^ ^ ^
| m_cursor m_limit
m_start
m_content
*/
void fill_line_buffer()
{
// number of processed characters (p)
const auto offset_start = m_start - m_content;
const auto offset_marker = m_marker - m_start;
// offset for m_marker wrt. to m_start
const auto offset_marker = (m_marker == nullptr) ? 0 : m_marker - m_start;
// number of unprocessed characters (u)
const auto offset_cursor = m_cursor - m_start;
m_buffer.erase(0, static_cast<size_t>(offset_start));
std::string line;
assert(m_stream != nullptr);
std::getline(*m_stream, line);
m_buffer += "\n" + line; // add line with newline symbol
// no stream is used or end of file is reached
if (m_stream == nullptr or not * m_stream)
{
// copy unprocessed characters to line buffer
m_line_buffer.clear();
for (m_cursor = m_start; m_cursor != m_limit; ++m_cursor)
{
m_line_buffer.append(1, static_cast<const char>(*m_cursor));
}
m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
// append 5 characters (size of longest keyword "false") to
// make sure that there is sufficient space between m_cursor
// and m_limit
m_line_buffer.append(5, '\0');
}
else
{
// delete processed characters from line buffer
m_line_buffer.erase(0, static_cast<size_t>(offset_start));
// read next line from input stream
std::string line;
std::getline(*m_stream, line);
// add line with newline symbol to the line buffer
m_line_buffer += "\n" + line;
}
// set pointers
m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.c_str());
assert(m_content != nullptr);
m_start = m_content;
m_marker = m_start + offset_marker;
m_cursor = m_start + offset_cursor;
m_limit = m_start + m_buffer.size() - 1;
m_limit = m_start + m_line_buffer.size();
}
/// return string representation of last read token
@ -8112,8 +8262,8 @@ class basic_json
private:
/// optional input stream
std::istream* m_stream = nullptr;
/// the buffer
string_t m_buffer;
/// line buffer buffer for m_stream
string_t m_line_buffer {};
/// the buffer pointer
const lexer_char_t* m_content = nullptr;
/// pointer to the beginning of the current symbol
@ -8136,25 +8286,34 @@ class basic_json
class parser
{
public:
/// constructor for strings
parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
: callback(cb), m_lexer(s)
{
// read first token
get_token();
}
/// a parser reading from a string literal
parser(const char* buff, const parser_callback_t cb = nullptr)
: callback(cb),
m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff), strlen(buff))
{}
/// a parser reading from an input stream
parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept
: callback(cb), m_lexer(&_is)
{
// read first token
get_token();
}
parser(std::istream& is, const parser_callback_t cb = nullptr)
: callback(cb), m_lexer(is)
{}
/// a parser reading from an iterator range with contiguous storage
template<class IteratorType, typename std::enable_if<
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
, int>::type
= 0>
parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
: callback(cb),
m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
static_cast<size_t>(std::distance(first, last)))
{}
/// public parser interface
basic_json parse()
{
// read first token
get_token();
basic_json result = parse_internal(true);
result.assert_invariant();
@ -8361,7 +8520,7 @@ class basic_json
}
/// get next token from lexer
typename lexer::token_type get_token() noexcept
typename lexer::token_type get_token()
{
last_token = m_lexer.scan();
return last_token;
@ -9659,7 +9818,7 @@ namespace std
@since version 1.0.0
*/
template <>
template<>
inline void swap(nlohmann::json& j1,
nlohmann::json& j2) noexcept(
is_nothrow_move_constructible<nlohmann::json>::value and
@ -9670,7 +9829,7 @@ inline void swap(nlohmann::json& j1,
}
/// hash value for JSON objects
template <>
template<>
struct hash<nlohmann::json>
{
/*!
@ -9701,7 +9860,7 @@ if no parse error occurred.
*/
inline nlohmann::json operator "" _json(const char* s, std::size_t)
{
return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
return nlohmann::json::parse(s);
}
/*!

View File

@ -38,43 +38,67 @@ TEST_CASE("lexer class")
{
SECTION("structural characters")
{
CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array);
CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array);
CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object);
CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object);
CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator);
CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
1).scan() == json::lexer::token_type::begin_array);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
1).scan() == json::lexer::token_type::end_array);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
1).scan() == json::lexer::token_type::begin_object);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"),
1).scan() == json::lexer::token_type::end_object);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","),
1).scan() == json::lexer::token_type::value_separator);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"),
1).scan() == json::lexer::token_type::name_separator);
}
SECTION("literal names")
{
CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null);
CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true);
CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
4).scan() == json::lexer::token_type::literal_null);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"),
4).scan() == json::lexer::token_type::literal_true);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"),
5).scan() == json::lexer::token_type::literal_false);
}
SECTION("numbers")
{
CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
1).scan() == json::lexer::token_type::value_number);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
1).scan() == json::lexer::token_type::value_number);
}
SECTION("whitespace")
{
// result is end_of_input, because not token is following
CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"),
1).scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "),
7).scan() == json::lexer::token_type::end_of_input);
}
}
@ -100,7 +124,11 @@ TEST_CASE("lexer class")
{
for (int c = 1; c < 128; ++c)
{
auto s = std::string(1, c);
// create string from the ASCII code
const auto s = std::string(1, c);
// store scan() result
const auto res = json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(s.c_str()),
1).scan();
switch (c)
{
@ -122,7 +150,7 @@ TEST_CASE("lexer class")
case ('8'):
case ('9'):
{
CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error);
CHECK(res != json::lexer::token_type::parse_error);
break;
}
@ -132,14 +160,14 @@ TEST_CASE("lexer class")
case ('\n'):
case ('\r'):
{
CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input);
CHECK(res == json::lexer::token_type::end_of_input);
break;
}
// anything else is not expected
default:
{
CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error);
CHECK(res == json::lexer::token_type::parse_error);
break;
}
}

View File

@ -32,6 +32,8 @@ SOFTWARE.
#include "json.hpp"
using nlohmann::json;
#include <valarray>
TEST_CASE("parser class")
{
SECTION("parse")
@ -487,7 +489,7 @@ TEST_CASE("parser class")
case ('r'):
case ('t'):
{
CHECK_NOTHROW(json::parser(s).parse());
CHECK_NOTHROW(json::parser(s.c_str()).parse());
break;
}
@ -500,8 +502,8 @@ TEST_CASE("parser class")
// any other combination of backslash and character is invalid
default:
{
CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_AS(json::parser(s.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser(s.c_str()).parse(), "parse error - unexpected '\"'");
break;
}
}
@ -559,22 +561,22 @@ TEST_CASE("parser class")
if (valid(c))
{
CHECK_NOTHROW(json::parser(s1).parse());
CHECK_NOTHROW(json::parser(s2).parse());
CHECK_NOTHROW(json::parser(s3).parse());
CHECK_NOTHROW(json::parser(s4).parse());
CHECK_NOTHROW(json::parser(s1.c_str()).parse());
CHECK_NOTHROW(json::parser(s2.c_str()).parse());
CHECK_NOTHROW(json::parser(s3.c_str()).parse());
CHECK_NOTHROW(json::parser(s4.c_str()).parse());
}
else
{
CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s1.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s2.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s3.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s4.c_str()).parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s1.c_str()).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s2.c_str()).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s3.c_str()).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s4.c_str()).parse(), "parse error - unexpected '\"'");
}
}
}
@ -755,11 +757,47 @@ TEST_CASE("parser class")
}
}
SECTION("copy constructor")
SECTION("constructing from contiguous containers")
{
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}));
SECTION("from std::vector")
{
std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
}
SECTION("from std::array")
{
std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
}
SECTION("from array")
{
uint8_t v[] = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
}
SECTION("from char literal")
{
CHECK(json::parser("true").parse() == json(true));
}
SECTION("from std::string")
{
std::string v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
}
SECTION("from std::initializer_list")
{
std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
}
SECTION("from std::valarray")
{
std::valarray<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
}
}
}

View File

@ -31,6 +31,8 @@ SOFTWARE.
#include "json.hpp"
using nlohmann::json;
#include <valarray>
TEST_CASE("deserialization")
{
SECTION("successful deserialization")
@ -43,7 +45,14 @@ TEST_CASE("deserialization")
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("string")
SECTION("string literal")
{
auto s = "[\"foo\",1,2,3,false,{\"one\":1}]";
json j = json::parse(s);
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("string_t")
{
json::string_t s = "[\"foo\",1,2,3,false,{\"one\":1}]";
json j = json::parse(s);
@ -74,7 +83,7 @@ TEST_CASE("deserialization")
}
}
SECTION("successful deserialization")
SECTION("unsuccessful deserialization")
{
SECTION("stream")
{
@ -116,4 +125,103 @@ TEST_CASE("deserialization")
"parse error - unexpected end of input; expected ']'");
}
}
SECTION("contiguous containers")
{
SECTION("directly")
{
SECTION("from std::vector")
{
std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parse(v) == json(true));
}
SECTION("from std::array")
{
std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
CHECK(json::parse(v) == json(true));
}
SECTION("from array")
{
uint8_t v[] = {'t', 'r', 'u', 'e'};
CHECK(json::parse(v) == json(true));
}
SECTION("from chars")
{
uint8_t *v = new uint8_t[5];
v[0] = 't';
v[1] = 'r';
v[2] = 'u';
v[3] = 'e';
v[4] = '\0';
CHECK(json::parse(v) == json(true));
delete[] v;
}
SECTION("from std::string")
{
std::string v = {'t', 'r', 'u', 'e'};
CHECK(json::parse(v) == json(true));
}
SECTION("from std::initializer_list")
{
std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parse(v) == json(true));
}
SECTION("empty container")
{
std::vector<uint8_t> v;
CHECK_THROWS_AS(json::parse(v), std::invalid_argument);
}
}
SECTION("via iterator range")
{
SECTION("from std::vector")
{
std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
}
SECTION("from std::array")
{
std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
}
SECTION("from array")
{
uint8_t v[] = {'t', 'r', 'u', 'e'};
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
}
SECTION("from std::string")
{
std::string v = {'t', 'r', 'u', 'e'};
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
}
SECTION("from std::initializer_list")
{
std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
}
SECTION("from std::valarray")
{
std::valarray<uint8_t> v = {'t', 'r', 'u', 'e'};
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
}
SECTION("with empty range")
{
std::vector<uint8_t> v;
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), std::invalid_argument);
}
}
}
}