Skip to content

JSON for Modern C++ version 3.0.0

Compare
Choose a tag to compare
@nlohmann nlohmann released this 17 Dec 10:08
· 2358 commits to develop since this release
afebb6a

Release date: 2017-12-17
SHA-256: 076d4a0cb890a3c3d389c68421a11c3d77c64bd788e85d50f1b77ed252f2a462

Summary

After almost a year, here is finally a new release of JSON for Modern C++, and it is a major one! As we adhere to semantic versioning, this means the release includes some breaking changes, so please read the next section carefully before you update. But don't worry, we also added a few new features and put a lot of effort into fixing a lot of bugs and straighten out a few inconsistencies.

💥 Breaking changes

This section describes changes that change the public API of the library and may require changes in code using a previous version of the library. In section "Moving from 2.x.x to 3.0.0" at the end of the release notes, we describe in detail how existing code needs to be changed.

  • The library now uses user-defined exceptions instead of re-using those defined in <stdexcept> (#244). This not only allows to add more information to the exceptions (every exception now has an identifier, and parse errors contain the position of the error), but also to easily catch all library exceptions with a single catch(json::exception).
  • When strings with a different encoding as UTF-8 were stored in JSON values, their serialization could not be parsed by the library itself, as only UTF-8 is supported. To enforce this library limitation and improve consistency, non-UTF-8 encoded strings now yield a json::type_error exception during serialization (#838). The check for valid UTF-8 is realized with code from Björn Hoehrmann.
  • NaN and infinity values can now be stored inside the JSON value without throwing an exception. They are, however, still serialized as null (#388).
  • The library's iterator tag was changed from RandomAccessIterator to BidirectionalIterator (#593). Supporting RandomAccessIterator was incorrect as it assumed an ordering of values in a JSON objects which are unordered by definition.
  • The library does not include the standard headers <iostream>, <ctype>, and <stdexcept> any more. You may need to add these headers to code relying on them.
  • Removed constructor explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) which was deprecated in version 2.0.0 (#480).

🔥 Deprecated functions

To unify the interfaces and to improve similarity with the STL, the following functions are now deprecated and will be removed in the next major version (i.e., 4.0.0):

Please use friend std::istream& operator>>(std::istream&, basic_json&) and friend operator<<(std::ostream&, const basic_json&) instead.

✨ New features

With all this breaking and deprecation out of the way, let's talk about features!

  • We improved the diagnostic information for syntax errors (#301). Now, an exception json::parse_error is thrown which contains a detailed message on the error, but also a member byte to indicate the byte offset in the input where the error occurred.
  • We added a non-throwing syntax check (#458): The new accept function returns a Boolean indicating whether the input is proper JSON. We also added a Boolean parameter allow_exceptions to the existing parse functions to return a discarded value in case a syntax error occurs instead of throwing an exception.
  • An update function was added to merge two JSON objects (#428). In case you are wondering: the name was inspired by Python.
  • The insert function now also supports an iterator range to add elements to an object.
  • The binary exchange formats CBOR and MessagePack can now be parsed from input streams and written to output streams (#477).
  • Input streams are now only read until the end of a JSON value instead of the end of the input (#367).
  • The serialization function dump now has two optional parameters ensure_ascii to escape all non-ASCII characters with \uxxxx and an indent_char parameter to choose whether to indent with spaces or tabs (#654).
  • Added built-in type support for C arrays (#502), std::pair and std::tuple (#563, #614), enum and enum class (#545), std::vector<bool> (#494). Fixed support for std::valarray (#702), std::array (#553), and std::map<std::string, std::string> (#600, #607).

🔨 Further changes

Furthermore, there have been a lot of changes under the hood:

  • Replaced the re2c generated scanner by a self-coded version which allows for a better modularization of the parser and better diagnostics. To test the new scanner, we added millions (8,860,608 to be exact) of unit tests to check all valid and invalid byte sequences of the Unicode standard.
  • Google's OSS-Fuzz is still constantly fuzz-testing the library and found several issues that were fixed in this release (#497, #504, #514, #516, #518, #519, #575).
  • We now also ignore UTF-8 byte order marks when parsing from an iterator range (#602).
  • Values can be now moved from initializer lists (#663).
  • Updated to Catch 1.9.7. Unfortunately, Catch2 currently has some performance issues.
  • The non-exceptional paths of the library are now annotated with __builtin_expect to optimize branch prediction as long as no error occurs.
  • MSVC now produces a stack trace in MSVC if a from_json or to_json function was not found for a user-defined type. We also added a debug visualizer nlohmann_json.natvis for better debugging in MSVC (#844).
  • Overworked the documentation and added even more examples.
  • The build workflow now relies on CMake and CTest. Special flags can be chosen with CMake, including coverage (JSON_Coverage), compilation without exceptions (JSON_NoExceptions), LLVM sanitizers (JSON_Sanitizer), or execution with Valgrind (JSON_Valgrind).
  • Added support for package managers Meson (#576), Conan (#566), Hunter (#671, #829), and vcpkg (#753).
  • Added CI builders: Xcode 8.3, 9.0, 9.1, and 9.2; GCC 7.2; Clang 3.8, 3.9, 4.0, and 5.0; Visual Studio 2017. The library is further built with C++17 settings on the latest Clang, GCC, and MSVC version to quickly detect new issues.

Moving from 2.x.x to 3.0.0

User-defined Exceptions

There are five different exceptions inheriting from json::exception:

To support these exception, the try/catch blocks of your code need to be adjusted:

new exception previous exception
parse_error.101 invalid_argument
parse_error.102 invalid_argument
parse_error.103 invalid_argument
parse_error.104 invalid_argument
parse_error.105 invalid_argument
parse_error.106 domain_error
parse_error.107 domain_error
parse_error.108 domain_error
parse_error.109 invalid_argument
parse_error.110 out_of_range
parse_error.111 invalid_argument
parse_error.112 invalid_argument
invalid_iterator.201 domain_error
invalid_iterator.202 domain_error
invalid_iterator.203 domain_error
invalid_iterator.204 out_of_range
invalid_iterator.205 out_of_range
invalid_iterator.206 domain_error
invalid_iterator.207 domain_error
invalid_iterator.208 domain_error
invalid_iterator.209 domain_error
invalid_iterator.210 domain_error
invalid_iterator.211 domain_error
invalid_iterator.212 domain_error
invalid_iterator.213 domain_error
invalid_iterator.214 out_of_range
type_error.301 domain_error
type_error.302 domain_error
type_error.303 domain_error
type_error.304 domain_error
type_error.305 domain_error
type_error.306 domain_error
type_error.307 domain_error
type_error.308 domain_error
type_error.309 domain_error
type_error.310 domain_error
type_error.311 domain_error
type_error.313 domain_error
type_error.314 domain_error
type_error.315 domain_error
out_of_range.401 out_of_range
out_of_range.402 out_of_range
out_of_range.403 out_of_range
out_of_range.404 out_of_range
out_of_range.405 domain_error
other_error.501 domain_error

Handling of NaN and INF

  • If an overflow occurs during parsing a number from a JSON text, an exception json::out_of_range is thrown so that the overflow is detected early and roundtripping is guaranteed.

  • NaN and INF floating-point values can be stored in a JSON value and are not replaced by null. That is, the basic_json class behaves like double in this regard (no exception occurs). However, NaN and INF are serialized to null.

Removal of deprecated functions

Function explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) should be replaced by the parse function: Let ss be a stream and cb be a parse callback function.

Old code:

json j(ss, cb);

New code:

json j = json::parse(ss, cb);

If no callback function is used, also the following code works:

json j;
j << ss;

or

json j;
ss >> j;