JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
writer.hpp
Go to the documentation of this file.
1/// \file jsonv/writer.hpp
2/// Write a JSON AST.
3///
4/// Copyright (c) 2026 by Travis Gockel. All rights reserved.
5///
6/// This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
7/// as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
8/// version.
9///
10/// \author Travis Gockel (travis@gockelhut.com)
11#pragma once
12
13#include <jsonv/config.hpp>
14#include <jsonv/forward.hpp>
15
16#include <cstddef>
17#include <cstdint>
18#include <iosfwd>
19#include <memory>
20#include <string_view>
21
22namespace jsonv
23{
24
25/// \addtogroup Serialization
26/// \{
27
28/// A writer instance writes a JSON \ref ast_node sequence to some form of sink: an \c encoder, which turns the tokens
29/// into text or into whatever else it builds.
30///
31/// This is the mirror of \c reader. A reader is a forward cursor over the tokens of a document somebody else wrote; a
32/// writer is a forward cursor over the tokens of the document you are writing. Each token method writes one token and
33/// returns \c *this, so calls chain. Writing an object means opening it, writing each member as a key followed by its
34/// value, and closing it:
35///
36/// \code
37/// struct my_object
38/// {
39/// std::int64_t a = 0;
40/// std::vector<std::string> tags;
41/// };
42///
43/// void write_my_object(jsonv::writer& to, const my_object& from)
44/// {
45/// to.object_begin();
46/// to.key("a").integer(from.a);
47/// to.key("tags").array_begin();
48/// for (const auto& tag : from.tags)
49/// to.string(tag);
50/// to.array_end();
51/// to.object_end();
52/// }
53///
54/// jsonv::ostream_pretty_encoder sink(std::cout);
55/// jsonv::writer to(sink);
56/// write_my_object(to, my_object{ 1, { "x", "y" } });
57/// \endcode
58///
59/// The writer owns the grammar and the punctuation. It refuses a token the grammar does not allow where the cursor is
60/// -- a key outside an object, a value in an object with no key before it, an end which does not match the open
61/// structure -- with \c std::logic_error, before the sink sees anything; and it writes the delimiters between elements
62/// and between members itself, so an \c encoder only ever sees a sequence of tokens which spells a valid document. A
63/// writer at depth zero accepts another root value, which is how two documents end up in one stream.
64///
65/// \see encoder
66/// \see reader
68{
69public:
70 /// Create a writer which writes into \a to. The encoder must outlive this instance.
71 explicit writer(encoder& to);
72
73 /// Create a writer which writes compact JSON text into \a to, as \c to_string does for a \c value: through an
74 /// \c ostream_encoder it owns, with \c ostream_encoder::ensure_ascii on. To pretty-print, or to write well-formed
75 /// UTF-8 as it is, construct the encoder yourself and use the overload above. The stream must outlive this
76 /// instance.
77 explicit writer(std::ostream& to);
78
79 // Not copyable.
80 writer(const writer&) = delete;
81 writer& operator=(const writer&) = delete;
82
83 /// \{
84
85 /// Moving a writer transfers its state and its sink, leaving the source moved-from: \c good is \c false, \c depth
86 /// is \c 0 and every other member throws \c std::invalid_argument. These are out-of-line because destroying the
87 /// state needs a complete \c writer::impl, which this header does not have -- the same reason the destructor is.
88 writer(writer&&) noexcept;
89 writer& operator=(writer&&) noexcept;
90 /// \}
91
92 ~writer() noexcept;
93
94 /// Check if this writer is still good to write to, which is to say that it has not been moved-from.
96 bool good() const noexcept;
97
98 /// The number of structures -- objects and arrays -- currently open. This is \c 0 before the first token, between
99 /// root values, and for a moved-from writer.
101 std::size_t depth() const noexcept;
102
103 /// Get the path of the slot the next token fills.
104 ///
105 /// \code
106 /// jsonv::writer to(sink); /* "." -- the root */
107 /// to.object_begin(); /* "." -- the object fills the root; the next token is a key, which has no slot */
108 /// to.key("a"); /* ".a" -- the value of "a" comes next */
109 /// to.array_begin(); /* ".a[0]" -- the first element comes next */
110 /// to.integer(1); /* ".a[1]" */
111 /// to.integer(2); /* ".a[2]" */
112 /// to.array_end(); /* "." -- back in the object, where the next token is a key */
113 /// to.key("b"); /* ".b" */
114 /// to.object_begin(); /* ".b" */
115 /// to.key("x"); /* ".b.x" */
116 /// to.string("taco"); /* ".b" */
117 /// to.object_end(); /* "." */
118 /// to.object_end(); /* "." -- the document is complete; another root may follow */
119 /// \endcode
120 ///
121 /// This is where a \c serializer is when it finds it cannot write the value it was asked for, which is what the
122 /// path is for. It is built from the stack of open structures on demand and kept until the next token, so a
123 /// document which never asks never pays for it -- unlike \c reader::current_path over text, which rescans the
124 /// document. The convention differs from the reader's, which names the token it is *on*: a reader on an element
125 /// and a writer about to write that element agree, as do the two at a key, while a reader on a closing token names
126 /// the structure it closes and a writer which has just closed one names where its next token goes.
127 ///
128 /// \throws std::invalid_argument if this instance has been moved-from.
130 const path& current_path() const;
131
132 /// \{
133
134 /// Write the token which opens or closes an object or an array.
135 ///
136 /// \code
137 /// {
138 /// \endcode
139 ///
140 /// Opening one is writing a value, so it is allowed wherever a value is: at depth zero, as an array element, or as
141 /// the value of the \c key just written. Closing one must match the innermost open structure, and an object cannot
142 /// close while a key is waiting for its value.
143 ///
144 /// \throws std::logic_error if the grammar does not allow the token here. Nothing has reached the sink.
145 /// \throws std::invalid_argument if this instance has been moved-from.
146 writer& object_begin();
147 writer& object_end();
148 writer& array_begin();
149 writer& array_end();
150 /// \}
151
152 /// Write the \a key of the next member of the open object, including the separator.
153 ///
154 /// \code
155 /// "key":
156 /// \endcode
157 ///
158 /// The value must follow before the next key or the end of the object. \a key is viewed, not kept: it has to live
159 /// until this returns and no longer.
160 ///
161 /// \throws std::logic_error if no object is open, if the innermost open structure is an array, or if the key before
162 /// this one is still waiting for its value. Nothing has reached the sink.
163 /// \throws std::invalid_argument if this instance has been moved-from.
164 writer& key(std::string_view key);
165
166 /// \{
167
168 /// Write a scalar value.
169 ///
170 /// \code
171 /// null
172 /// true
173 /// 902
174 /// 4.9
175 /// "value"
176 /// \endcode
177 ///
178 /// A value is allowed at depth zero, as an array element, or as the value of the \c key just written. What a
179 /// \c decimal with no JSON representation -- a NaN or an infinity -- becomes is the encoder's choice, as is what
180 /// happens to a \c string which is not valid UTF-8 (see \c encoder::write_decimal and \c encoder::write_string).
181 ///
182 /// \throws std::logic_error if an object is open and no key is waiting for its value. Nothing has reached the sink.
183 /// \throws std::invalid_argument if this instance has been moved-from.
184 writer& null();
185 writer& boolean(bool value);
186 writer& integer(std::int64_t value);
187 writer& decimal(double value);
188 writer& string(std::string_view value);
189 /// \}
190
191 /// Write the whole of \a source as one value, as \c encoder::encode does. An \c encoder producing text walks it,
192 /// writing every node in order with an object's members in the order the object keeps them, which is sorted by
193 /// key; one which builds a tree copies it as it is rather than rebuilding it node by node.
194 ///
195 /// \throws std::logic_error if a value is not allowed here, as for the scalar functions. Nothing has reached the
196 /// sink.
197 /// \throws std::invalid_argument if this instance has been moved-from.
198 writer& write(const value& source);
199
200 /// Write the whole of \a source as one value, handing it over to the sink. An \c encoder which builds a tree takes
201 /// it as it is rather than copying it, and one producing text walks it exactly as the overload above does.
202 /// \a source is left moved-from.
203 ///
204 /// \throws std::logic_error if a value is not allowed here, as for the scalar functions. Nothing has reached the
205 /// sink and \a source is untouched.
206 /// \throws std::invalid_argument if this instance has been moved-from.
207 writer& write(value&& source);
208
209private:
210 class impl;
211
212private:
213 std::unique_ptr<impl> _impl;
214};
215
216/// \}
217
218}
An encoder is responsible for writing values to some form of output.
Definition encode.hpp:35
Represents an exact path in some JSON structure.
Definition path.hpp:107
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:113
A writer instance writes a JSON ast_node sequence to some form of sink: an encoder,...
Definition writer.hpp:68
writer(std::ostream &to)
Create a writer which writes compact JSON text into to, as to_string does for a value: through an ost...
writer(writer &&) noexcept
Moving a writer transfers its state and its sink, leaving the source moved-from: good is false,...
writer(encoder &to)
Create a writer which writes into to. The encoder must outlive this instance.
Copyright (c) 2014-2020 by Travis Gockel.
Copyright (c) 2012-2020 by Travis Gockel.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
Definition config.hpp:132
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:113
STL namespace.