Skip to content

toughengineer/minjsoncpp

Repository files navigation

Minimalistic JSON C++ library

minjsoncpp is a minimalistic C++17 header-only library that implements facilities to programmatically manipulate, serialize/stringify and parse JSON data as defined by RFC 8259 The JavaScript Object Notation (JSON) Data Interchange Format.

The goals of this project (from the highest priority to the lowest):

  1. strictly conform to JSON specification,
  2. minimal footprint (source size, binary size, etc.),
  3. reasonable performance.

As such the code is only 1500 lines, no dependencies except for the C++ standard library, yet it follows the spec very closely and has reasonably good performance compared to popular JSON C++ libraries.

It is primarily intended for situations when you want to read a small JSON file, a config file for example, or to write a small JSON file, but don't want to deal with huge and/or complicated dependencies because of that small part where you need such functionality.

Supported compilers and standard libraries

Compilers:

  • gcc: 11.1 and later*
  • Clang: 8 (with libstdc++ 11.1..12.3) and later (with respectively supported versions of standard library)
  • MSVC: 19.16 (comes with Visual Studio 2017 version 15.9.11) and later

Standard libraries:

  • libstdc++: 11.1 and later*
    (goes with GCC, can be used with Clang)
  • libc++: 14 and later**
    (goes with Clang)
  • MSVC: the one that comes with Visual Studio 2017 version 15.9.11 and later
    (can be used with Clang on Windows)

*Since the earliest supported version of libstdc++ is 11.1 that's the corresponding earliest supported version of GCC because they (typically) come together.
**libc++ does not implement std::from_chars() for double; strtod() is used instead when building with libc++.

In general you should be able to use it with any fully C++17 conformant compiler and standard library.

How to acquire the library

The easiest way is to put the header file minjsoncpp.h or copy-paste its contents into your project.

Cloning repo/using submodule

You can clone the repo https://github.com/toughengineer/minjsoncpp.git, or add it as a submodule.

Click/tap to expand details.
git submodule add https://github.com/toughengineer/minjsoncpp.git

or

git submodule add https://github.com/toughengineer/minjsoncpp.git thirdparty/minjsoncpp

to specify destination directory, in this case thirdparty/minjsoncpp,
and then initialize it with

git submodule update --init

In your CMakeLists.txt you can just add include directory with something like this:

include_directories("thirdparty/minjsoncpp/include")

Or you can add include directory for a particular CMake target (${TARGET_NAME} in this case):

target_include_directories(${TARGET_NAME} PRIVATE "thirdparty/minjsoncpp/include")

In this case you don't need to explicitly add dependencies to the targets.

Instead of explicitly adding include directory you can add a dependency to your CMake target:

target_link_libraries(${TARGET_NAME} minjsoncpp)

add_subdirectory("thirdparty/minjsoncpp")

target_link_libraries adds dependency to a target and makes include file available.

add_subdirectory processes minjsoncpp's CMakeLists.txt and adds minjsoncpp target for consumption.

Using Cmake FetchContent

You can use FetchContent to get the library via CMakeLists.txt:

include(FetchContent)

FetchContent_Declare(
  minjsoncpp
  GIT_REPOSITORY https://github.com/toughengineer/minjsoncpp.git
  GIT_TAG main
)

FetchContent_MakeAvailable(minjsoncpp)

For your CMake target add:

target_link_libraries(${TARGET_NAME} minjsoncpp)

where ${TARGET_NAME} is the name of a CMake target like an executable or a library.

API

It is relatively straightforward.

You can jump straight to examples if you want to see how it can be used.

This library assumes UTF-8 encoding of strings inside JSON values, of inputs that are parsed, and of serialized/stringified JSON representation prescribed by RFC 8259 section 8.1.

Entities in the namespace minjson are all public and can be used freely.

Entities in the namespace minjson::impl are also public, but should be seen as "advanced" API intended to expose the full functionality without conveniently defined defaults.

Entities in the namespace minjson::detail are implementation details, you should not use them explicitly.

JSON values

JSON value is represented as template minjson::BasicValue<Allocator> with allocator parameter.

minjson::Value is defined for covenience as an alias to minjson::BasicValue<std::allocator<char>>. See section about allocator support below for details.

By convention minjson::BasicValue is parameterized with an allocator type for char elements.

Assuming minjson::Value, mapping of JSON value types to C++ types is as follows:

JSON C++
null minjson::Null (alias for std::monostate)
boolean minjson::Boolean (alias for bool)
number int64_t for integers,
double for decimal numbers
string minjson::String (alias for std::string, assumed to contain UTF-8 text)
array minjson::Array (alias for std::vector<minjson::Value>)
object minjson::Object (alias for std::unordered_map<minjson::String, minjson::Value>)

Concrete value is stored in a variant defined as:

using Variant = std::variant<Null, Boolean, int64_t, double, String, Array, Object>;

minjson::Value provides access to it via variant() method and can be implicitly converted to a reference to minjson::Variant.

visit() functionality

You can std::visit() the underlying variant as usual, e.g.:

minjson::Value value = foo();
std::visit([](auto &x) { bar(x); }, value.variant());

or use minjson::visit() function directly on a minjson::Value instance:

minjson::Value value = foo();
minjson::visit([](auto &x) { bar(x); }, value);

or you can omit the namespace and rely on ADL:

minjson::Value value = foo();
visit([](auto &x) { bar(x); }, value);

See visit.cpp for an example.

Value type inspection

You can explicitly check the type of a value and access the underlying object by using the following methods:

check type access value
isNull()
isBool() asBool()
isInt() asInt()
isDouble() asDouble()
isString() asString()
isArray() asArray()
isObject() asObject()

Initialization

You can construct minjson::Value in the usual way, e.g.:

minjson::Value null = minjson::Null{};
minjson::Value boolean = true;
minjson::Value integer = 42;
minjson::Value decimal = 3.14;
minjson::Value string = "hello";
minjson::Value array = minjson::Array{ 1, 2, 3 };
minjson::Value object = minjson::Object{ { "foo", 42 }, { "bar", "baz" } };

You can combine it any way you want, e.g.:

const minjson::Value value = minjson::Object{
  { "null", minjson::Null{} },
  { "boolean", true },
  { "integer", 42 },
  { "decimal", 3.14 },
  { "string", "hello there"sv },
  { "array", minjson::Array{ 1, 2, 3 } },
  { "object", minjson::Object{
      { "nested number", 23 },
      { "nested string", "General Kenobi"sv },
      { "nested array", minjson::Array{ 4, 5, 6 } },
      { "nested object", minjson::Object{ { "foo", "bar"sv } } }
    }
  }
};

Accessing values throughout JSON document

To access values within the JSON document throughout multiple levels you can use resolve() method which returns a pointer to a value (const minjson::Value*, can be nullptr if resolution fails).

Below are some examples of usage.

minjson::Value o = minjson::Object{ { "foo", 42 }, { "bar", "baz" } };

const auto *foo = o.resolve("foo");
if (foo) {
  // use '*foo'
}

In this case, string key argument "foo" implies that we expect o to contain an object and we are trying to get member value designated by the corresponding key.

minjson::Value a = minjson::Array{ 1, 2, 3 };

const auto *item = a.resolve(0);
if (item) {
  // use '*item'
}

Here numeric index argument 0 implies that we expect a to contain an array and we are trying to get member value with the corresponding index.

You can provide multiple arguments to resolve values throughout multiple levels, which would be similar to arguments being tokens of a JSON Pointer string, e.g.

const minjson::Value value = minjson::Object{
  { "object", minjson::Object{
      { "nested array", minjson::Array{ 4, 5, 6 } }
    }
  }
};

const auto *item = value.resolve("object", "nested array", 0);
if (item) {
  // use '*item'
}

In this case

  • string key argument "object" implies that we expect value to contain an object and we are trying to get member value designated by that key,
  • in turn, string key argument "nested array" implies that we expect the value that we get from the previous step to be an object and we are trying to get member value designated by this key,
  • finally, numeric index argument 0 implies that we expect the value that we get from the previous step (the "nested array" one) to contain an array and we are trying to get member value with that index.

This would be similar to trying to resolve JSON Pointer /object/nested array/1.

See resolve.cpp for more examples.

Sinks

Function templates in namespace minjson::impl receive "sinks": objects with implemented operator() or functions that can receive instances of std::string_view as subsequent parts of the result.

E.g. it can be a lambda:

const auto stdOutSink = [](std::string_view s) { std::cout << s; };

In this case it outputs everything into stdout.

Or

std::string string;
const auto stringSink = [&string](std::string_view s) { string += s; };

In this case the result is subsequently appended to string.

Escaping strings

namespace minjson {
  enum class Escape {
    Default,
    NonAscii
  };
}
  • Default: escape only mandated characters \x0..\x1F, " (\x22) and \ (\x5C).
  • NonAscii: escape mandated caracters as well as non-ASCII characters (code points \x80..\x10FFFF).
    Note that escaped characters will always occupy larger space (more bytes) compared to characters as is (when they are not escaped).

namespace minjson {
  enum class Utf8Validation {
    IgnoreInvalidUtf8CodeUnits,
    FailOnInvalidUtf8CodeUnits
  };
}
  • IgnoreInvalidUtf8CodeUnits: turns UTF-8 validation off.
  • FailOnInvalidUtf8CodeUnits: operation fails when an invalid UTF-8 code point/code unit is encountered.

namespace minjson {
  enum class HexDigitsCase {
    Lower,
    Upper
  };
}

minjson::HexDigitsCase specifies hex digits case in escapes (e.g. "\u00ae").


namespace minjson {
  template<typename String_t = String>
  [[nodiscard]] String_t escape(std::string_view s,
                                Escape escapeMode = {},
                                Utf8Validation validation = {},
                                HexDigitsCase hexDigitsCase = HexDigitsCase::Lower);
}

minjson::escape() returns escaped input string. String type must support operator+= with std::string_view operand and reserve() method receiving size_t.

When minjson::Utf8Validation::FailOnInvalidUtf8CodeUnits is specified, returns empty string on failure.
Otherwise fails only if underlying string appending fails, e.g. as a result of failed allocation.

See escape.cpp for an example.


namespace minjson {
  namespace impl {
    template<typename Sink>
    size_t escape(Sink &&sink,
                  std::string_view s,
                  Escape escapeMode,
                  Utf8Validation validation,
                  HexDigitsCase hexDigitsCase);
  }
}

minjson::impl::escape() passes subsequent parts of escaped string to the sink.

Returns

  • the size in bytes/code units of the input string that was successfully escaped, or
  • the offset (in bytes/code units) of invalid code point on failure when minjson::Utf8Validation::FailOnInvalidUtf8CodeUnits is specified.

This function does not throw exceptions on its own.
Exceptions thrown by the sink operations are let through, i.e. are not handled.

See escape_impl.cpp for an example.


Serialization

namespace minjson {
  struct SerializationOptions {
    size_t indent = 0;
    Escape escape = {};
    Utf8Validation validation = {};
    HexDigitsCase hexDigitsCase = HexDigitsCase::Lower;
    enum class IndentationChar {
      Space,
      Tab
    } indentationChar = {};
    struct NewlineOptions {
      std::string_view separator = {};
      bool afterObjectOpeningBrace = true;
      bool beforeObjectClosingBrace = true;
      bool afterObjectMemberKey = false;
      bool beforeObjectMemberValue = false;
      bool beforeObjectMemberCollectionValue = false;
      bool beforeObjectMemberSeparator = false;
      bool afterObjectMemberSeparator = true;
      bool afterArrayOpeningBracket = true;
      bool beforeArrayClosingBracket = true;
      bool beforeArrayMemberSeparator = false;
      bool afterArrayMemberSeparator = true;
    } newline{};
    bool sortObjectKeys = false;
    std::string_view nullLiteral = NullLiteral;
    std::string_view falseLiteral = FalseLiteral;
    std::string_view trueLiteral = TrueLiteral;
    std::string_view emptyObject = "{}";
    std::string_view objectOpeningBrace = "{";
    std::string_view objectClosingBrace = "}";
    std::string_view objectKeyValueSeparator = ":";
    std::string_view objectMemberSeparator = ",";
    std::string_view emptyArray = "[]";
    std::string_view arrayOpeningBracket = "[";
    std::string_view arrayClosingBracket = "]";
    std::string_view arrayMemberSeparator = ",";
    std::string_view openingStringQuotation = "\"";
    std::string_view closingStringQuotation = "\"";
  };
}

minjson::SerializationOptions defines options to control serialization/stringification.

indent specifies the amount of spaces or tabs to use to indent nested values (everything inside an object {} or an array [] is indented).

escape, validation and hexDigitsCase specify the same things as in the case of escape functions above.

indentationChar tells to use spaces for indentation when IndentationChar::Space is set, and tabs when IndentationChar::Tab is set.

newline.separator specifies line separator like \n or \r\n.
If empty and indent is set to non-zero, defaults to \n.

The rest of the newline options specify after or before which element to insert new line and indentation. (Click/tap to show.)

newline.afterObjectOpeningBrace designates new line after {, e.g.

{
  "foo":"bar"}

newline.beforeObjectClosingBrace designates new line before }, e.g.

{"foo":"bar"
}

If either newline.afterObjectOpeningBrace or newline.beforeObjectClosingBrace is set and emptyObject is set to an empty string, empty objects are serialized like this:

{
}

newline.afterObjectMemberKey designates new line after key before :, e.g.

{"foo"
  :"bar"}

newline.beforeObjectMemberValue designates new line after : before value, e.g.

{"foo":
  "bar"}

newline.beforeObjectMemberCollectionValue designates new line after : before an array or an object, e.g.

{"foo":"bar",
  "baz":
  ["qux"]}

newline.beforeObjectMemberSeparator designates new line before , inside objects, e.g.

{"foo":"bar"
  ,"baz":"qux"}

newline.afterObjectMemberSeparator designates new line after , inside objects, e.g.

{"foo":"bar",
  "baz":"qux"}

newline.afterArrayOpeningBracket designates new line after [, e.g.

[
  "foo","bar"]

newline.beforeArrayClosingBracket designates new line before ], e.g.

["foo","bar"
]

If either newline.afterArrayOpeningBracket or newline.beforeArrayClosingBracket is set and emptyArray is set to an empty string, empty arrays are serialized like this:

[
]

newline.beforeArrayMemberSeparator designates new line before , inside arrays, e.g.

["foo"
  ,"bar"]

newline.afterArrayMemberSeparator designates new line after , inside arrays, e.g.

["foo",
  "bar"]

sortObjectKeys tells to serialize object members in ascending order of keys when set to true.

The following options allow to customize elements of JSON when serializing. (Click/tap to show.)
options description
nullLiteral literal null
falseLiteral literal false
trueLiteral literal true
emptyObject used for empty objects if set to a non-empty string, e.g. {}
objectOpeningBrace {
objectClosingBrace }
objectKeyValueSeparator :
objectMemberSeparator , inside objects
emptyArray used for empty arrays if set to a non-empty string, e.g. []
arrayOpeningBracket [
arrayClosingBracket ]
arrayMemberSeparator , inside arrays
openingStringQuotation string opening "
closingStringQuotation string closing "

namespace minjson {
  struct InvalidUtf8CodeUnitsError : std::runtime_error {
    InvalidUtf8CodeUnitsError(const char *msg,
                              std::string_view codeUnits,
                              size_t offset);
    const std::string codeUnits;
    const size_t offset;
  };
}

minjson::InvalidUtf8CodeUnitsError is thrown by serialize() functions when UTF-8 validation fails.

codeUnits field contains code units at the point of failure from the string that caused it.

offset specifies the offset of codeUnits in the string that caused failure.

Note

When minjson::SerializationOptions::validation is set to minjson::Utf8Validation::FailOnInvalidUtf8CodeUnits all the serialization functions minjson::serializeToStream(), minjson::serializeToString() and minjson::impl::serialize() throw minjson::InvalidUtf8CodeUnitsError on validation failure.


namespace minjson {
  template<typename Allocator>
  void serializeToStream(std::ostream &s,
                         const BasicValue<Allocator> &v,
                         const SerializationOptions &o = {});
}

minjson::serializeToStream() writes serialized JSON value v to standard output stream s.

May throw minjson::InvalidUtf8CodeUnitsError.
Otherwise fails only if the underlying stream write operation fails.


namespace minjson {
  template<typename Allocator>
  [[nodiscard]] typename BasicValue<Allocator>::String serializeToString(const BasicValue<Allocator> &v,
                                                                         const SerializationOptions &o = {});
}

minjson::serializeToString() returns JSON value v serialized to a string.

May throw minjson::InvalidUtf8CodeUnitsError.
Otherwise fails only if underlying string appending fails, e.g. as a result of failed allocation.

See serialize.cpp for an example.


namespace minjson {
  namespace impl {
    template<typename Sink, typename Allocator>
    void serialize(Sink &&sink,
                   const BasicValue<Allocator> &v,
                   const SerializationOptions &options,
                   size_t initialIndentation = 0);
  }
}

minjson::impl::serialize() passes subsequent parts of serialized JSON value v to the sink.

initialIndentation specifies initial indentation of nested collection members. Note that the root value itself is not indented.

May throw minjson::InvalidUtf8CodeUnitsError.
Exceptions thrown by the sink operations are let through, i.e. are not handled.

See serialize_impl.cpp for an example.


Unescaping strings

namespace minjson {
  enum class UnescapeMode {
    Relaxed,
    Strict
  };
}
  • Relaxed mode allows input to have any characters.
  • Strict mode allows only valid JSON string characters to be in the input, i.e. code points \x20..\x10FFFF; " (\x22) must be escaped.

In both modes \ (\x5C) must introduce a valid escape sequence, e.g.: "\n".


namespace minjson {
  template<typename String_t = String>
  [[nodiscard]] String_t unescape(std::string_view input,
                                  UnescapeMode unescapeMode = {});
}

minjson::unescape() returns string containing unescaped input, or empty string on failure.

Fails on invalid escape sequences, and invalid JSON string characters if minjson::UnescapeMode::Strict is specified.
Otherwise fails only if underlying string appending fails, e.g. as a result of failed allocation.

See unescape.cpp for an example.


namespace minjson {
  template<typename String_t = String>
  [[nodiscard]] String_t unescape(std::string_view input,
                                  UnescapeMode unescapeMode,
                                  char32_t unpairedSurrogateReplacement);
}

minjson::unescape() returns string containing unescaped input, or empty string on failure.

unpairedSurrogateReplacement specifies replacement character for incorrectly situated UTF-16 surrogates, e.g. when high surrogate is not followed by low surrogate (hence not meaningfully encoding a character): "\ud83d".

Fails on invalid escape sequences, and invalid JSON string characters if minjson::UnescapeMode::Strict is specified.
Otherwise fails only if underlying string appending fails, e.g. as a result of failed allocation.


namespace minjson {
  namespace impl {
    inline constexpr size_t NPos = ~size_t{ 0 };

    inline constexpr char32_t DoNotReplaceSurrogates = ~char32_t{ 0 };

    template<typename Sink>
    size_t unescape(Sink &&sink,
                    std::string_view input,
                    UnescapeMode mode,
                    char32_t surrogateReplacement = DoNotReplaceSurrogates);
  }
}

minjson::impl::unescape() passes subsequent parts of unescaped input to the sink.

Returns

  • the size in bytes/code units of the input that was successfully unescaped (size of input on success), or
  • minjson::impl::NPos if end of input was reached unexpectedly, e.g. in the middle of an escape: "\u0".

surrogateReplacement specifies replacement character for incorrectly situated UTF-16 surrogates. If set to minjson::impl::DoNotReplaceSurrogates causes unmatched surrogate code points to be encoded as UTF-8.

Fails on invalid escape sequences, and invalid JSON string characters if minjson::UnescapeMode::Strict is specified.
Exceptions thrown by the sink operations are let through, i.e. are not handled.

See unescape_impl.cpp for an example.


Parsing

namespace minjson {
  struct ParsingOptions {
    enum class Option {
      Ignore,
      Report,
      Fail
    };
    Option duplicateObjectKeys = Option::Fail;
    Option unpairedUtf16Surrogates = Option::Ignore;
    bool replaceInvalidUtf16Surrogates = false;
    char32_t replacement = U'\xfffd';
  };
}

minjson::ParsingOptions defines options to control parsing behavior.

duplicateObjectKeys tells what to do when duplicate keys are encountered in an object during parsing:

  • Option::Ignore: the resulting value is set to one of the encountered values;
  • Option::Report: additionally the issue is added to the list of issues;
  • Option::Fail: parsing fails when duplicate keys are encountered, the issue is added to the list of issues.

unpairedUtf16Surrogates tells what to do when an unpaired UTF-16 surrogate is encountered during parsing (e.g. when high surrogate is not followed by low surrogate hence not meaningfully encoding a character: "\ud83d"):

  • Option::Ignore: no validation is performed;
  • Option::Report: the issue is added to the list of issues;
  • Option::Fail: parsing fails when an unpaired surrogate is encounterd, the issue is added to the list of issues.

replaceInvalidUtf16Surrogates specifies whether to replace incorrectly situated surrogates.

replacement specifies replacement character for incorrectly situated surrogates, by default it's U+FFFD: �.


namespace minjson {
  struct ParsingIssue {
    enum class Code {
      Other,
      InvalidCharacter,
      UnexpectedEndOfInput,
      FailedToParseNumber,
      ParsedNumberOutOfRange,
      DuplicateKeys,
      StringContainsUnpairedUtf16HighSurrogate,
      StringContainsUnpairedUtf16LowSurrogate
    };
    size_t offset;
    std::string_view description;
    Code code = {};
  };
}

minjson::ParsingIssue bundles information about a parsing issue.

offset designates

  • offset in bytes/code units of characters that caused the issue, or
  • equals to the input size if unexpected end of input is encountered.

description contains textual human friendly description of the issue.

code contains a code from minjson::ParsingIssue::Code enumeration for distinguishing of the issue cause.
FailedToParseNumber in theory should never happen but is included in case a particular implementation of std::from_chars() (and libc++'s strtod()) fails for some unexpected reason.


namespace minjson {
  enum class ParsingResultStatus {
    Failure,
    Success,
    PartialSuccess
  };
}

minjson::ParsingResultStatus signifies the result of parsing. See descriptions of individual parse() functions below.


namespace minjson {
  template<typename Allocator>
  struct BasicParsingResult {
    using Status = ParsingResultStatus;
    using Issues = std::vector<ParsingIssue,
      typename std::allocator_traits<Allocator>::template rebind_alloc<ParsingIssue>>;

    BasicValue<Allocator> value;
    Status status;
    size_t parsedSize;
    Issues issues;
  };

  using ParsingResult = BasicParsingResult<std::allocator<char>>;
}

minjson::BasicParsingResult template instantiation is returned by parsing functions.

value contains parsed JSON value in case of success.

status contains parsing result status, e.g. success or failure.

parsedSize constains size in bytes/code units of the input that was successfully parsed. Note that if end of input was reached unexpectedly it contains the size of the input.

issues contains a list of issues encountered during parsing.

minjson::ParsingResult alias is defined for convenience.


namespace minjson {
  enum class ParsingMode {
    VerifyTrailingWhitespace,
    StopAfterValueEnds
  };
}
  • VerifyTrailingWhitespace tells minjson::parse() to verify trailing whitespaces up to the end of input.
  • StopAfterValueEnds stops parsing immediately after a valid JSON value.

namespace minjson {
  template<typename Allocator = std::allocator<char>>
  [[nodiscard]] BasicParsingResult<Allocator> parse(std::string_view input,
                                                    const ParsingOptions &options = {},
                                                    ParsingMode parsingMode = ParsingMode::VerifyTrailingWhitespace,
                                                    const Allocator &allocator = {});
}

minjson::parse() parses input string, returns an instance of minjson::BasicParsingResult with the result.

When minjson::ParsingMode::VerifyTrailingWhitespace is specified, in case input contains non-whitespace characters after a valid JSON value, result's status field is set to minjson::ParsingResultStatus::PartialSuccess and an issue is added to the issues list.

Provided allocator is used for all memory allocations.

minjson::parse() does not handle byte order mark, it is not skipped and is treated as invalid characters.

This function does not throw exceptions on its own.
Otherwise fails only if underlying entity operation fails, e.g. as a result of failed allocation.

See parse.cpp for an example.


namespace minjson {
  namespace impl {
    template<typename Allocator>
    [[nodiscard]] BasicParsingResult<Allocator> parse(std::string_view input,
                                                      const ParsingOptions &options,
                                                      const Allocator &allocator);
  }
}

minjson::impl::parse() parses input string, returns an instance of minjson::BasicParsingResult with the result.

Contrary to minjson::parse() always stops parsing immediately after a valid JSON value.
This function never sets result's status to minjson::ParsingResultStatus::PartialSuccess.

Provided allocator is used for all memory allocations.

minjson::impl::parse() does not handle byte order mark, it is not skipped and is treated as invalid characters.

This function does not throw exceptions on its own.
Otherwise fails only if underlying entity operation fails, e.g. as a result of failed allocation.

See parse_impl.cpp for an example.


Polymorphic sinks

In order not to bloat the generated binary code with many types of sinks, you can use polymorphic sinks, e.g.

std::function<void(const std::string_view&)>

You can specify it with minjson::impl versions of the functions, e.g. making a wrapper:

template<typename String_t = std::string>
[[nodiscard]] String_t myEscape(std::string_view input,
                                minjson::Escape escapeMode = {},
                                minjson::Utf8Validation validation = {},
                                minjson::HexDigitsCase hexDigitsCase = minjson::HexDigitsCase::Lower) {
  String_t s;
  if (validation == minjson::Utf8Validation::IgnoreInvalidUtf8CodeUnits)
    s.reserve(s.size());
  const size_t unescapedSize = minjson::impl::escape<std::function<void(const std::string_view&)>>(
    [&s](std::string_view t) { s += t; },
    input,
    escapeMode,
    validation,
    hexDigitsCase);
  return unescapedSize == input.size() ? std::move(s) : String_t{};
}

This way minjson::impl::escape() function template is always instantiated with parameter std::function<void(const std::string_view&)> regardless of the string type.

Allocator support

Support for Allocator types

minjson::BasicValue<Allocator> template does not allocate memory for scalar types like bool or int64_t.
However it uses an allocator for its stored string, array and object values.

You can instantiate minjson::BasicValue with a custom allocator, e.g. std::pmr::polymorphic_allocator.

Allocator type is supposed to satisfy Allocator requirements.

By convention, in order not to multiply instantiations, minjson::BasicValue is instantiated with an allocator type parameterized with char type, e.g. a convenient alias for specialization with std::allocator is already provided in the following form:

namespace minjson {
  using Value = BasicValue<std::allocator<char>>;
}

You can define your own specialization like this:

using PmrValue = BasicValue<std::pmr::polymorphic_allocator<char>>;

Allocator awareness

std::basic_string, std::vector and std::unordered_map are all allocator aware containers.

minjson::BasicValue is allocator aware in the sense that it bridges stored container's allocator awareness functionality (see AllocatorAwareContainer named requirements) and the outside world. E.g. when stored in allocator aware containers (e.g. minjson::Array which is an alias for std::vector<minjson::Value>) allocators are properly propagated into minjson::Value and in turn into string, array and object values stored in each particular instance, i.e. it's turtles allocator propagation all the way down.

License

This software is licensed under the MIT license (SPDX identifier: MIT), see LICENSE file for full text.

As an exception minjsoncpp.h is available under the MIT No Attribution license (SPDX identifier: MIT-0).