Corrade::Utility::JsonWriter class new in Git master

JSON writer.

A counterpart to Json for saving JSON files, including whitespace control. Supports writing of 32-bit floating-point numbers and 32-bit and 52-/53-bit unsigned and signed integer types in addition to the general 64-bit floating-point representation.

To optimize for writing performance and minimal memory usage, the class provides a write-only stream-like interface, formatting the JSON string on the fly. It is thus not possible to go for example go back and add values to existing objects or arrays — if that's desired, one option is to use multiple JsonWriter instances and concatenate them together, as shown later on.

Usage

The following writes a very minimal glTF file, pretty-printed with two-space indentation. Objects are created with matching beginObject() and endObject() calls, writeKey() adds an object key and write() a value. Similarly, arrays are delimited with beginArray() and endArray(). For convenience the calls can be chained after each other.

Utility::JsonWriter gltf{
    Utility::JsonWriter::Option::Wrap|
    Utility::JsonWriter::Option::TypographicalSpace, 2};
gltf.beginObject()
        .writeKey("asset").beginObject()
            .writeKey("version").write("2.0")
        .endObject()
        .writeKey("nodes").beginArray()
            .beginObject()
                .writeKey("name").write("Chair")
                .writeKey("mesh").write(5)
            .endObject()
        .endArray()
    .endObject();
{
  "asset": {
    "version": "2.0"
  },
  "nodes": [
    {
      "name": "Fox",
      "mesh": 5
    }
  ]
}
if(!gltf.toFile("scene.gltf"))
    Utility::Fatal{} << "Huh, can't write a file?";

To avoid errors, each call checks that it's indeed made when given token is expected. The final toFile() or toString() can only be called once all objects and arrays are completed. While a JSON commonly has a top-level object or array, a single top-level literal, number or string is allowed as well. There has to be exactly one top-level value, empty files are not allowed.

Array and object scopes

The beginObjectScope() and beginArrayScope() functions return a Containers::ScopeGuard object that will automatically perform a matching call to endObject() or endArray() at the end of scope, which may be useful when writing deeply nested hierarchies:

struct Node {
    Containers::StringView name;
    unsigned mesh;
};
Containers::ArrayView<const Node> nodes;



{
    gltf.writeKey("nodes");
    Containers::ScopeGuard gltfNodes = gltf.beginArrayScope();

    for(const Node& node: nodes) {
        Containers::ScopeGuard gltfNode = gltf.beginObjectScope();

        gltf.writeKey("name").write(node.name)
            .writeKey("mesh").write(node.mesh);
    }
}

Writing numeric arrays

For convenience and more compact pretty-printed output, there's also a set of writeArray() functions for writing arrays of a homogeneous numeric type, each having an option to wrap after a certain number of elements. This snippet shows writing a glTF transformation matrix and a children list, and how a formatted output would look like:

struct Node {
    
    float matrix[16];
    Containers::Array<unsigned> children;
};


gltf.writeKey("matrix").writeArray(node.matrix, 4)
    .writeKey("children").writeArray(node.children);
{
  "matrix": [
    2, 0, 0, 0,
    0, 2, 0, 0,
    0, 0, 2, 0,
    0, 0, 0, 1
  ],
  "children": [3, 7]
}

Combining multiple writers together

While the streaming nature of the writer doesn't allow to add new values to multiple places in the file, this can be achieved by populating multiple JsonWriter instances and then combining their formatted output together using writeJson(). The following snippet first creates standalone glTF node and mesh arrays and then combines them together to a complete glTF file, with each node having exactly one assigned mesh:

Utility::JsonWriter gltfMeshes{
    Utility::JsonWriter::Option::Wrap|
    Utility::JsonWriter::Option::TypographicalSpace, 2, 2};
Utility::JsonWriter gltfNodes{
    Utility::JsonWriter::Option::Wrap|
    Utility::JsonWriter::Option::TypographicalSpace, 2, 2};

for(const Mesh& mesh: meshes) {
    /* Open the mesh and node arrays if they're empty */
    if(gltfMeshes.isEmpty()) gltfMeshes.beginArray();
    if(gltfNodes.isEmpty()) gltfNodes.beginArray();

    std::size_t gltfMeshId = gltfMeshes.currentArraySize();
    Containers::ScopeGuard gltfMesh = gltfMeshes.beginObjectScope();
    gltfMeshes.writeKey("name").write(mesh.name)
              .writeKey("mode").write(mesh.mode)
              ;

    Containers::ScopeGuard gltfNode = gltfNodes.beginObjectScope();
    gltfNodes.writeKey("name").write(mesh.name)
             .writeKey("mesh").write(gltfMeshId)
             ;
}

Utility::JsonWriter gltf{
    Utility::JsonWriter::Option::Wrap|
    Utility::JsonWriter::Option::TypographicalSpace, 2};
gltf.beginObject();

/* Close and add the mesh and node arrays if they're non-empty */
if(!gltfMeshes.isEmpty()) {
    gltfMeshes.endArray();
    gltf.writeKey("meshes").write(gltfMeshes.toString());
}
if(!gltfNodes.isEmpty()) {
    gltfNodes.endArray();
    gltf.writeKey("nodes").write(gltfNodes.toString());
}

gltf.endObject();

Note the initialIndentation parameter passed to the JsonWriter(Options, std::uint32_t, std::uint32_t) constructor, which will make indentation of the nested arrays match the surroundings in the final file. The currentArraySize() index is used to know the ID of the currently added mesh instead of having to increment a counter by hand, and finally isEmpty() is used to know whether there's any meshes at all, in which case the list is completely omitted in the final file.

Public types

enum class Option { Wrap = 1 << 0, TypographicalSpace = 1 << 1 }
Pretty-printing option.
using Options = Containers::EnumSet<Option>
Pretty-printing options.

Constructors, destructors, conversion operators

JsonWriter(Options options, std::uint32_t indentation, std::uint32_t initialIndentation = 0) explicit
Construct a pretty-printing JSON writer.
JsonWriter() explicit
Construct a compact JSON writer.
JsonWriter(const JsonWriter&) deleted
Copying is not allowed.
JsonWriter(JsonWriter&&) noexcept
Move constructor.
~JsonWriter()
Destructor.

Public functions

auto operator=(const JsonWriter&) -> JsonWriter& deleted
Copying is not allowed.
auto operator=(JsonWriter&&) -> JsonWriter& noexcept
Move assignment.
auto isEmpty() const -> bool
Whether the writer is empty.
auto size() const -> std::size_t
Byte size of the output written so far.
auto beginObject() -> JsonWriter&
Begin an object.
auto endObject() -> JsonWriter&
End an object.
auto beginArray() -> JsonWriter&
Begin an array.
auto endArray() -> JsonWriter&
End an array.
auto beginObjectScope() -> Containers::ScopeGuard
Begin an object scope.
auto beginArrayScope() -> Containers::ScopeGuard
Begin an object scope.
auto currentArraySize() const -> std::size_t
Size of the currently written array.
auto writeKey(Containers::StringView key) -> JsonWriter&
Write an object key.
auto write(std::nullptr_t) -> JsonWriter&
Write a null value.
auto writeValue(bool value) -> JsonWriter&
Write a bool value.
auto write(float value) -> JsonWriter&
Write a 32-bit floating-point value.
auto write(double value) -> JsonWriter&
Write a 64-bit floating-point value.
auto write(std::uint32_t value) -> JsonWriter&
Write an unsigned 32-bit integer value.
auto write(std::int32_t value) -> JsonWriter&
Write a signed 32-bit integer value.
auto write(std::uint64_t value) -> JsonWriter&
Write an unsigned 52-bit integer value.
auto write(std::int64_t value) -> JsonWriter&
Write a signed 53-bit integer value.
auto write(Containers::StringView value) -> JsonWriter&
Write a string value.
auto writeArray(Containers::StridedArrayView1D<const float> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
Write a 32-bit floating-point array.
auto writeArray(std::initializer_list<float> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
auto writeArray(Containers::StridedArrayView1D<const double> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
Write a 64-bit floating-point array.
auto writeArray(std::initializer_list<double> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
auto writeArray(Containers::StridedArrayView1D<const std::uint32_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
Write an unsigned 32-bit integer array.
auto writeArray(std::initializer_list<std::uint32_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
auto writeArray(Containers::StridedArrayView1D<const std::int32_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
Write a signed 32-bit integer array.
auto writeArray(std::initializer_list<std::int32_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
auto writeArray(Containers::StridedArrayView1D<const std::uint64_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
Write an unsigned 52-bit integer array.
auto writeArray(std::initializer_list<std::uint64_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
auto writeArray(Containers::StridedArrayView1D<const std::int64_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
Write a signed 53-bit integer array.
auto writeArray(std::initializer_list<std::int64_t> values, std::uint32_t wrapAfter = 0) -> JsonWriter&
auto writeJson(Containers::StringView json) -> JsonWriter&
Write a raw JSON string.
auto toString() const -> Containers::StringView
Get the result as a string.
auto toFile(Containers::StringView filename) const -> bool
Save the result into a file.

Enum documentation

enum class Corrade::Utility::JsonWriter::Option

Pretty-printing option.

Enumerators
Wrap

Wrap object and array contents. Turns

[[1,2,null],"hello",{"key":"value","another":true}]

into the following:

[
  [
    1,
    2,
    null
  ],
  "hello",
  {
     "key":"value",
     "another":true
  }
]

If initialIndentation is zero, the formatted output is treated as a top-level JSON document and includes also a final newline at the end. Otherwise it's assumed that the output will be subsequently put inside another JSON document and thus a newline is not added as it may break the formatting.

The writeArray() APIs use a different, more compact formatting, with the wrapAfter argument specifying after how many values to wrap. In the following case, a nested array of 8 values is printed, wrapping after the fourth:

{
  "name":"numbers",
  "data":[
    1.17,2.35,0.24,5,
    2.67,-1.0,2.2,1.5
  ]
}

With wrapAfter set to 0, it would instead look like this:

{
  "name":"numbers",
  "data":[1.17,2.35,0.24,5,2.67,-1.0,2.2,1.5]
}

Indentation before object keys and array values is controlled with the indentation parameter passed to the JsonWriter(Options, std::uint32_t, std::uint32_t) constructor. Nested object and array values use one indentation level more for their contents. Use Option::TypographicalSpace to add a space after the : in object keys.

TypographicalSpace

Puts a typographical space after : in object keys, and also after , if not immediately followed by a newline coming from Option::Wrap. Turns

[[1,2,null],"hello",{"key":"value","another":true}]

into the following:

[[1, 2, null], "hello", {"key": "value", "another": true}]

No spaces are added before a :, before , or inside [] and {} braces.

Typedef documentation

typedef Containers::EnumSet<Option> Corrade::Utility::JsonWriter::Options

Pretty-printing options.

Function documentation

Corrade::Utility::JsonWriter::JsonWriter(Options options, std::uint32_t indentation, std::uint32_t initialIndentation = 0) explicit

Construct a pretty-printing JSON writer.

Parameters
options Pretty-printing options
indentation Number of spaces used for each indentation level. Has no effect if Option::Wrap is not set. Expected to be at most 8.
initialIndentation Number of spaces used for initial indentation of second and following lines, useful when combining output of multiple writers together. Has no effect if Option::Wrap is not set.

Corrade::Utility::JsonWriter::JsonWriter() explicit

Construct a compact JSON writer.

Equivalent to calling JsonWriter(Options, std::uint32_t, std::uint32_t) with an empty Options and 0 for both indentation and initialIndentation.

Corrade::Utility::JsonWriter::~JsonWriter()

Destructor.

Compared to toString() or toFile(), it isn't an error if a writer instance with an incomplete JSON gets destructed.

bool Corrade::Utility::JsonWriter::isEmpty() const

Whether the writer is empty.

Returns true if no bytes were written yet — i.e., if no beginObject() / beginObjectScope(), beginArray() / beginArrayScope(), writeKey(), write() or writeJson() was called yet.

std::size_t Corrade::Utility::JsonWriter::size() const

Byte size of the output written so far.

Unlike toString() or toFile(), this function can be called at any point, even if the top-level JSON value isn't completely written yet. When the top-level value is complete, the returned size is equal to size of the data returned from toString() and toFile().

JsonWriter& Corrade::Utility::JsonWriter::beginObject()

Begin an object.

Returns Reference to self (for method chaining)

Writes { to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected.

JsonWriter& Corrade::Utility::JsonWriter::endObject()

End an object.

Returns Reference to self (for method chaining)

Writes } to the output, with spacing and indentation as appropriate. Expected to be called only if beginObject() was called before with no unclosed array in the meantime and not when an object value is expected.

JsonWriter& Corrade::Utility::JsonWriter::beginArray()

Begin an array.

Returns Reference to self (for method chaining)

Writes [ to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected.

JsonWriter& Corrade::Utility::JsonWriter::endArray()

End an array.

Returns Reference to self (for method chaining)

Writes ] to the output, with spacing and indentation as appropriate. Expected to be called only if beginArray() was called before with no unclosed object in the meantime.

Containers::ScopeGuard Corrade::Utility::JsonWriter::beginObjectScope()

Begin an object scope.

Calls beginObject() and returns a scope guard instance that calls endObject() at the end of the scope. See Array and object scopes for an example.

Containers::ScopeGuard Corrade::Utility::JsonWriter::beginArrayScope()

Begin an object scope.

Calls beginArray() and returns a scope guard instance that calls endArray() at the end of the scope. See Array and object scopes for an example.

std::size_t Corrade::Utility::JsonWriter::currentArraySize() const

Size of the currently written array.

Returns the count of values present so far in a currently written array. In case of nested arrays returns a size of the leaf array, to get size of the parent arrays you have to endArray() the children first or query the size before a beginArray() call of the child.

Expects that an array is currently being written — i.e., beginArray() / beginArrayScope() was recently called with no endArray() or beginObject() call happening after.

JsonWriter& Corrade::Utility::JsonWriter::writeKey(Containers::StringView key)

Write an object key.

Returns Reference to self (for method chaining)

Writes the key as a JSON string literal to the output, separated by , if there's another value before, followed by a :, with spacing and indentation as appropriate. Expected to be called only inside an object scope either at the beginning or after a value for the previous key was written. Escaping behavior is the same as with write(Containers::StringView).

JsonWriter& Corrade::Utility::JsonWriter::write(std::nullptr_t)

Write a null value.

Returns Reference to self (for method chaining)

Writes null to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected.

JsonWriter& Corrade::Utility::JsonWriter::writeValue(bool value)

Write a bool value.

Returns Reference to self (for method chaining)

Writes false or true to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected.

JsonWriter& Corrade::Utility::JsonWriter::write(float value)

Write a 32-bit floating-point value.

Returns Reference to self (for method chaining)

Writes the value as a JSON number literal to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected. The value is expected to not be a NaN or an infinity and is printed with 6 significant digits, consistently with Debug or format(). If you need a larger precision, use write(double).

JsonWriter& Corrade::Utility::JsonWriter::write(double value)

Write a 64-bit floating-point value.

Returns Reference to self (for method chaining)

Writes the value as a JSON number literal to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected. The value is expected to not be a NaN or an infinity and is printed with 15 significant digits, consistently with Debug or format().

JsonWriter& Corrade::Utility::JsonWriter::write(std::uint32_t value)

Write an unsigned 32-bit integer value.

Returns Reference to self (for method chaining)

Writes the value as a JSON number literal to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected.

JsonWriter& Corrade::Utility::JsonWriter::write(std::int32_t value)

Write a signed 32-bit integer value.

Returns Reference to self (for method chaining)

Writes the value as a JSON number literal to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected.

JsonWriter& Corrade::Utility::JsonWriter::write(std::uint64_t value)

Write an unsigned 52-bit integer value.

Returns Reference to self (for method chaining)

Writes the value as a JSON number literal to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected. The value is expected to fit into 52 bits, which is the representable unsigned integer range in a JSON.

JsonWriter& Corrade::Utility::JsonWriter::write(std::int64_t value)

Write a signed 53-bit integer value.

Returns Reference to self (for method chaining)

Writes the value as a JSON number literal to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected. The value is expected to fit into 52 bits, excluding the sign, which is the representable signed integer range in a JSON.

JsonWriter& Corrade::Utility::JsonWriter::write(Containers::StringView value)

Write a string value.

Returns Reference to self (for method chaining)

Writes the value as a JSON string literal to the output, separated by , if there's another value before, with spacing and indentation as appropriate. Expected to not be called after the top-level JSON value was closed and not when an object key is expected — use writeKey() in that case instead. The string is expected to be in UTF-8 but its validity isn't checked. Only the ", \, bell ('\b'), form feed ('\f'), newline ('\n'), tab ('\t') and carriage return ('\r') values are escaped, the / character and UTF-8 bytes are written verbatim without escaping.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(Containers::StridedArrayView1D<const float> values, std::uint32_t wrapAfter = 0)

Write a 32-bit floating-point array.

Returns Reference to self (for method chaining)

A compact shorthand for calling beginArray(), followed by zero or more write(float) calls, followed by endArray(). See documentation of these functions for more information. Compared to these functions however, different pretty-printing rules are used — see Writing numeric arrays and Option::Wrap for details. If Option::Wrap is not set, wrapAfter is ignored.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(std::initializer_list<float> values, std::uint32_t wrapAfter = 0)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(Containers::StridedArrayView1D<const double> values, std::uint32_t wrapAfter = 0)

Write a 64-bit floating-point array.

Returns Reference to self (for method chaining)

A compact shorthand for calling beginArray(), followed by zero or more write(double) calls, followed by endArray(). See documentation of these functions for more information. Compared to these functions however, different pretty-printing rules are used — see Writing numeric arrays and Option::Wrap for details. If Option::Wrap is not set, wrapAfter is ignored.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(std::initializer_list<double> values, std::uint32_t wrapAfter = 0)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(Containers::StridedArrayView1D<const std::uint32_t> values, std::uint32_t wrapAfter = 0)

Write an unsigned 32-bit integer array.

Returns Reference to self (for method chaining)

A compact shorthand for calling beginArray(), followed by zero or more write(std::uint32_t) calls, followed by endArray(). See documentation of these functions for more information. Compared to these functions however, different pretty-printing rules are used — see Writing numeric arrays and Option::Wrap for details. If Option::Wrap is not set, wrapAfter is ignored.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(std::initializer_list<std::uint32_t> values, std::uint32_t wrapAfter = 0)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(Containers::StridedArrayView1D<const std::int32_t> values, std::uint32_t wrapAfter = 0)

Write a signed 32-bit integer array.

Returns Reference to self (for method chaining)

A compact shorthand for calling beginArray(), followed by zero or more write(std::int32_t) calls, followed by endArray(). See documentation of these functions for more information. Compared to these functions however, different pretty-printing rules are used — see Writing numeric arrays and Option::Wrap for details. If Option::Wrap is not set, wrapAfter is ignored.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(std::initializer_list<std::int32_t> values, std::uint32_t wrapAfter = 0)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(Containers::StridedArrayView1D<const std::uint64_t> values, std::uint32_t wrapAfter = 0)

Write an unsigned 52-bit integer array.

Returns Reference to self (for method chaining)

A compact shorthand for calling beginArray(), followed by zero or more write(std::uint64_t) calls, followed by endArray(). See documentation of these functions for more information. Compared to these functions however, different pretty-printing rules are used — see Writing numeric arrays and Option::Wrap for details. If Option::Wrap is not set, wrapAfter is ignored.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(std::initializer_list<std::uint64_t> values, std::uint32_t wrapAfter = 0)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(Containers::StridedArrayView1D<const std::int64_t> values, std::uint32_t wrapAfter = 0)

Write a signed 53-bit integer array.

Returns Reference to self (for method chaining)

A compact shorthand for calling beginArray(), followed by zero or more write(std::int64_t) calls, followed by endArray(). See documentation of these functions for more information. Compared to these functions however, different pretty-printing rules are used — see Writing numeric arrays and Option::Wrap for details. If Option::Wrap is not set, wrapAfter is ignored.

JsonWriter& Corrade::Utility::JsonWriter::writeArray(std::initializer_list<std::int64_t> values, std::uint32_t wrapAfter = 0)

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

JsonWriter& Corrade::Utility::JsonWriter::writeJson(Containers::StringView json)

Write a raw JSON string.

Returns Reference to self (for method chaining)

The string is expected to be non-empty and a valid and closed JSON value, i.e., a null, bool numeric or a string literal, a complete object or a complete array, but its validity isn't checked. Internally it's treated as writing a single value, separated by , if there's another value before, with outside spacing and indentation as appropriate, but no spacing or indentation performed inside the string.

Containers::StringView Corrade::Utility::JsonWriter::toString() const

Get the result as a string.

Expected to be called only once a complete top-level JSON value is written. The returned view has Containers::StringViewFlag::NullTerminated set, points to data owned by the JsonWriter instance and is valid until the end of its lifetime.

bool Corrade::Utility::JsonWriter::toFile(Containers::StringView filename) const

Save the result into a file.

Expected to be called only once a complete top-level JSON value is written. Returns false if the file can't be written.