cannam@62
|
1 // Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
|
cannam@62
|
2 // Licensed under the MIT License:
|
cannam@62
|
3 //
|
cannam@62
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
cannam@62
|
5 // of this software and associated documentation files (the "Software"), to deal
|
cannam@62
|
6 // in the Software without restriction, including without limitation the rights
|
cannam@62
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
cannam@62
|
8 // copies of the Software, and to permit persons to whom the Software is
|
cannam@62
|
9 // furnished to do so, subject to the following conditions:
|
cannam@62
|
10 //
|
cannam@62
|
11 // The above copyright notice and this permission notice shall be included in
|
cannam@62
|
12 // all copies or substantial portions of the Software.
|
cannam@62
|
13 //
|
cannam@62
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
cannam@62
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
cannam@62
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
cannam@62
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
cannam@62
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
cannam@62
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
cannam@62
|
20 // THE SOFTWARE.
|
cannam@62
|
21
|
cannam@62
|
22 #ifndef CAPNP_COMPAT_JSON_H_
|
cannam@62
|
23 #define CAPNP_COMPAT_JSON_H_
|
cannam@62
|
24
|
cannam@62
|
25 #include <capnp/schema.h>
|
cannam@62
|
26 #include <capnp/dynamic.h>
|
cannam@62
|
27 #include <capnp/compat/json.capnp.h>
|
cannam@62
|
28
|
cannam@62
|
29 namespace capnp {
|
cannam@62
|
30
|
cannam@62
|
31 class JsonCodec {
|
cannam@62
|
32 // Flexible class for encoding Cap'n Proto types as JSON, and decoding JSON back to Cap'n Proto.
|
cannam@62
|
33 //
|
cannam@62
|
34 // Typical usage:
|
cannam@62
|
35 //
|
cannam@62
|
36 // JsonCodec json;
|
cannam@62
|
37 //
|
cannam@62
|
38 // // encode
|
cannam@62
|
39 // kj::String encoded = json.encode(someStructReader);
|
cannam@62
|
40 //
|
cannam@62
|
41 // // decode
|
cannam@62
|
42 // json.decode(encoded, someStructBuilder);
|
cannam@62
|
43 //
|
cannam@62
|
44 // Advanced users can do fancy things like override the way certain types or fields are
|
cannam@62
|
45 // represented in JSON by registering handlers. See the unit test for an example.
|
cannam@62
|
46 //
|
cannam@62
|
47 // Notes:
|
cannam@62
|
48 // - When encoding, all primitive fields are always encoded, even if default-valued. Pointer
|
cannam@62
|
49 // fields are only encoded if they are non-null.
|
cannam@62
|
50 // - 64-bit integers are encoded as strings, since JSON "numbers" are double-precision floating
|
cannam@62
|
51 // points which cannot store a 64-bit integer without losing data.
|
cannam@62
|
52 // - NaNs and infinite floating point numbers are not allowed by the JSON spec, and so are encoded
|
cannam@62
|
53 // as null. This matches the behavior of `JSON.stringify` in at least Firefox and Chrome.
|
cannam@62
|
54 // - Data is encoded as an array of numbers in the range [0,255]. You probably want to register
|
cannam@62
|
55 // a handler that does something better, like maybe base64 encoding, but there are a zillion
|
cannam@62
|
56 // different ways people do this.
|
cannam@62
|
57 // - Encoding/decoding capabilities and AnyPointers requires registering a Handler, since there's
|
cannam@62
|
58 // no obvious default behavior.
|
cannam@62
|
59 // - When decoding, unrecognized field names are ignored. Note: This means that JSON is NOT a
|
cannam@62
|
60 // good format for receiving input from a human. Consider `capnp eval` or the SchemaParser
|
cannam@62
|
61 // library for human input.
|
cannam@62
|
62
|
cannam@62
|
63 public:
|
cannam@62
|
64 JsonCodec();
|
cannam@62
|
65 ~JsonCodec() noexcept(false);
|
cannam@62
|
66
|
cannam@62
|
67 // ---------------------------------------------------------------------------
|
cannam@62
|
68 // standard API
|
cannam@62
|
69
|
cannam@62
|
70 void setPrettyPrint(bool enabled);
|
cannam@62
|
71 // Enable to insert newlines, indentation, and other extra spacing into the output. The default
|
cannam@62
|
72 // is to use minimal whitespace.
|
cannam@62
|
73
|
cannam@62
|
74 void setMaxNestingDepth(size_t maxNestingDepth);
|
cannam@62
|
75 // Set maximum nesting depth when decoding JSON to prevent highly nested input from overflowing
|
cannam@62
|
76 // the call stack. The default is 64.
|
cannam@62
|
77
|
cannam@62
|
78 template <typename T>
|
cannam@62
|
79 kj::String encode(T&& value);
|
cannam@62
|
80 // Encode any Cap'n Proto value to JSON, including primitives and
|
cannam@62
|
81 // Dynamic{Enum,Struct,List,Capability}, but not DynamicValue (see below).
|
cannam@62
|
82
|
cannam@62
|
83 kj::String encode(DynamicValue::Reader value, Type type) const;
|
cannam@62
|
84 // Encode a DynamicValue to JSON. `type` is needed because `DynamicValue` itself does
|
cannam@62
|
85 // not distinguish between e.g. int32 and int64, which in JSON are handled differently. Most
|
cannam@62
|
86 // of the time, though, you can use the single-argument templated version of `encode()` instead.
|
cannam@62
|
87
|
cannam@62
|
88 void decode(kj::ArrayPtr<const char> input, DynamicStruct::Builder output) const;
|
cannam@62
|
89 // Decode JSON text directly into a struct builder. This only works for structs since lists
|
cannam@62
|
90 // need to be allocated with the correct size in advance.
|
cannam@62
|
91 //
|
cannam@62
|
92 // (Remember that any Cap'n Proto struct reader type can be implicitly cast to
|
cannam@62
|
93 // DynamicStruct::Reader.)
|
cannam@62
|
94
|
cannam@62
|
95 template <typename T>
|
cannam@62
|
96 Orphan<T> decode(kj::ArrayPtr<const char> input, Orphanage orphanage) const;
|
cannam@62
|
97 // Decode JSON text to any Cap'n Proto object (pointer value), allocated using the given
|
cannam@62
|
98 // orphanage. T must be specified explicitly and cannot be dynamic, e.g.:
|
cannam@62
|
99 //
|
cannam@62
|
100 // Orphan<MyType> orphan = json.decode<MyType>(text, orphanage);
|
cannam@62
|
101
|
cannam@62
|
102 template <typename T>
|
cannam@62
|
103 ReaderFor<T> decode(kj::ArrayPtr<const char> input) const;
|
cannam@62
|
104 // Decode JSON text into a primitive or capability value. T must be specified explicitly and
|
cannam@62
|
105 // cannot be dynamic, e.g.:
|
cannam@62
|
106 //
|
cannam@62
|
107 // uint32_t n = json.decode<uint32_t>(text);
|
cannam@62
|
108
|
cannam@62
|
109 Orphan<DynamicValue> decode(kj::ArrayPtr<const char> input, Type type, Orphanage orphanage) const;
|
cannam@62
|
110 Orphan<DynamicList> decode(
|
cannam@62
|
111 kj::ArrayPtr<const char> input, ListSchema type, Orphanage orphanage) const;
|
cannam@62
|
112 Orphan<DynamicStruct> decode(
|
cannam@62
|
113 kj::ArrayPtr<const char> input, StructSchema type, Orphanage orphanage) const;
|
cannam@62
|
114 DynamicCapability::Client decode(kj::ArrayPtr<const char> input, InterfaceSchema type) const;
|
cannam@62
|
115 DynamicEnum decode(kj::ArrayPtr<const char> input, EnumSchema type) const;
|
cannam@62
|
116 // Decode to a dynamic value, specifying the type schema.
|
cannam@62
|
117
|
cannam@62
|
118 // ---------------------------------------------------------------------------
|
cannam@62
|
119 // layered API
|
cannam@62
|
120 //
|
cannam@62
|
121 // You can separate text <-> JsonValue from JsonValue <-> T. These are particularly useful
|
cannam@62
|
122 // for calling from Handler implementations.
|
cannam@62
|
123
|
cannam@62
|
124 kj::String encodeRaw(JsonValue::Reader value) const;
|
cannam@62
|
125 void decodeRaw(kj::ArrayPtr<const char> input, JsonValue::Builder output) const;
|
cannam@62
|
126 // Translate JsonValue <-> text.
|
cannam@62
|
127
|
cannam@62
|
128 template <typename T>
|
cannam@62
|
129 void encode(T&& value, JsonValue::Builder output);
|
cannam@62
|
130 void encode(DynamicValue::Reader input, Type type, JsonValue::Builder output) const;
|
cannam@62
|
131 void decode(JsonValue::Reader input, DynamicStruct::Builder output) const;
|
cannam@62
|
132 template <typename T>
|
cannam@62
|
133 Orphan<T> decode(JsonValue::Reader input, Orphanage orphanage) const;
|
cannam@62
|
134 template <typename T>
|
cannam@62
|
135 ReaderFor<T> decode(JsonValue::Reader input) const;
|
cannam@62
|
136
|
cannam@62
|
137 Orphan<DynamicValue> decode(JsonValue::Reader input, Type type, Orphanage orphanage) const;
|
cannam@62
|
138 Orphan<DynamicList> decode(JsonValue::Reader input, ListSchema type, Orphanage orphanage) const;
|
cannam@62
|
139 Orphan<DynamicStruct> decode(
|
cannam@62
|
140 JsonValue::Reader input, StructSchema type, Orphanage orphanage) const;
|
cannam@62
|
141 DynamicCapability::Client decode(JsonValue::Reader input, InterfaceSchema type) const;
|
cannam@62
|
142 DynamicEnum decode(JsonValue::Reader input, EnumSchema type) const;
|
cannam@62
|
143
|
cannam@62
|
144 // ---------------------------------------------------------------------------
|
cannam@62
|
145 // specializing particular types
|
cannam@62
|
146
|
cannam@62
|
147 template <typename T, Style s = style<T>()>
|
cannam@62
|
148 class Handler;
|
cannam@62
|
149 // Implement this interface to specify a special encoding for a particular type or field.
|
cannam@62
|
150 //
|
cannam@62
|
151 // The templates are a bit ugly, but subclasses of this type essentially implement two methods,
|
cannam@62
|
152 // one to encode values of this type and one to decode values of this type. `encode()` is simple:
|
cannam@62
|
153 //
|
cannam@62
|
154 // void encode(const JsonCodec& codec, ReaderFor<T> input, JsonValue::Builder output) const;
|
cannam@62
|
155 //
|
cannam@62
|
156 // `decode()` is a bit trickier. When T is a struct (including DynamicStruct), it is:
|
cannam@62
|
157 //
|
cannam@62
|
158 // void decode(const JsonCodec& codec, JsonValue::Reader input, BuilderFor<T> output) const;
|
cannam@62
|
159 //
|
cannam@62
|
160 // However, when T is a primitive, decode() is:
|
cannam@62
|
161 //
|
cannam@62
|
162 // T decode(const JsonCodec& codec, JsonValue::Reader input) const;
|
cannam@62
|
163 //
|
cannam@62
|
164 // Or when T is any non-struct object (list, blob), decode() is:
|
cannam@62
|
165 //
|
cannam@62
|
166 // Orphan<T> decode(const JsonCodec& codec, JsonValue::Reader input, Orphanage orphanage) const;
|
cannam@62
|
167 //
|
cannam@62
|
168 // Or when T is an interface:
|
cannam@62
|
169 //
|
cannam@62
|
170 // T::Client decode(const JsonCodec& codec, JsonValue::Reader input) const;
|
cannam@62
|
171 //
|
cannam@62
|
172 // Additionally, when T is a struct you can *optionally* also implement the orphan-returning form
|
cannam@62
|
173 // of decode(), but it will only be called when the struct would be allocated as an individual
|
cannam@62
|
174 // object, not as part of a list. This allows you to return "nullptr" in these cases to say that
|
cannam@62
|
175 // the pointer value should be null. This does not apply to list elements because struct list
|
cannam@62
|
176 // elements cannot ever be null (since Cap'n Proto encodes struct lists as a flat list rather
|
cannam@62
|
177 // than list-of-pointers).
|
cannam@62
|
178
|
cannam@62
|
179 template <typename T>
|
cannam@62
|
180 void addTypeHandler(Handler<T>& handler);
|
cannam@62
|
181 void addTypeHandler(Type type, Handler<DynamicValue>& handler);
|
cannam@62
|
182 void addTypeHandler(EnumSchema type, Handler<DynamicEnum>& handler);
|
cannam@62
|
183 void addTypeHandler(StructSchema type, Handler<DynamicStruct>& handler);
|
cannam@62
|
184 void addTypeHandler(ListSchema type, Handler<DynamicList>& handler);
|
cannam@62
|
185 void addTypeHandler(InterfaceSchema type, Handler<DynamicCapability>& handler);
|
cannam@62
|
186 // Arrange that whenever the type T appears in the message, your handler will be used to
|
cannam@62
|
187 // encode/decode it.
|
cannam@62
|
188 //
|
cannam@62
|
189 // Note that if you register a handler for a capability type, it will also apply to subtypes.
|
cannam@62
|
190 // Thus Handler<Capability> handles all capabilities.
|
cannam@62
|
191
|
cannam@62
|
192 template <typename T>
|
cannam@62
|
193 void addFieldHandler(StructSchema::Field field, Handler<T>& handler);
|
cannam@62
|
194 // Matches only the specific field. T can be a dynamic type. T must match the field's type.
|
cannam@62
|
195
|
cannam@62
|
196 private:
|
cannam@62
|
197 class HandlerBase;
|
cannam@62
|
198 struct Impl;
|
cannam@62
|
199
|
cannam@62
|
200 kj::Own<Impl> impl;
|
cannam@62
|
201
|
cannam@62
|
202 void encodeField(StructSchema::Field field, DynamicValue::Reader input,
|
cannam@62
|
203 JsonValue::Builder output) const;
|
cannam@62
|
204 void decodeArray(List<JsonValue>::Reader input, DynamicList::Builder output) const;
|
cannam@62
|
205 void decodeObject(List<JsonValue::Field>::Reader input, DynamicStruct::Builder output) const;
|
cannam@62
|
206 void addTypeHandlerImpl(Type type, HandlerBase& handler);
|
cannam@62
|
207 void addFieldHandlerImpl(StructSchema::Field field, Type type, HandlerBase& handler);
|
cannam@62
|
208 };
|
cannam@62
|
209
|
cannam@62
|
210 // =======================================================================================
|
cannam@62
|
211 // inline implementation details
|
cannam@62
|
212
|
cannam@62
|
213 template <typename T>
|
cannam@62
|
214 kj::String JsonCodec::encode(T&& value) {
|
cannam@62
|
215 typedef FromAny<kj::Decay<T>> Base;
|
cannam@62
|
216 return encode(DynamicValue::Reader(ReaderFor<Base>(kj::fwd<T>(value))), Type::from<Base>());
|
cannam@62
|
217 }
|
cannam@62
|
218
|
cannam@62
|
219 template <typename T>
|
cannam@62
|
220 inline Orphan<T> JsonCodec::decode(kj::ArrayPtr<const char> input, Orphanage orphanage) const {
|
cannam@62
|
221 return decode(input, Type::from<T>(), orphanage).template releaseAs<T>();
|
cannam@62
|
222 }
|
cannam@62
|
223
|
cannam@62
|
224 template <typename T>
|
cannam@62
|
225 inline ReaderFor<T> JsonCodec::decode(kj::ArrayPtr<const char> input) const {
|
cannam@62
|
226 static_assert(style<T>() == Style::PRIMITIVE || style<T>() == Style::CAPABILITY,
|
cannam@62
|
227 "must specify an orphanage to decode an object type");
|
cannam@62
|
228 return decode(input, Type::from<T>(), Orphanage()).getReader().template as<T>();
|
cannam@62
|
229 }
|
cannam@62
|
230
|
cannam@62
|
231 inline Orphan<DynamicList> JsonCodec::decode(
|
cannam@62
|
232 kj::ArrayPtr<const char> input, ListSchema type, Orphanage orphanage) const {
|
cannam@62
|
233 return decode(input, Type(type), orphanage).releaseAs<DynamicList>();
|
cannam@62
|
234 }
|
cannam@62
|
235 inline Orphan<DynamicStruct> JsonCodec::decode(
|
cannam@62
|
236 kj::ArrayPtr<const char> input, StructSchema type, Orphanage orphanage) const {
|
cannam@62
|
237 return decode(input, Type(type), orphanage).releaseAs<DynamicStruct>();
|
cannam@62
|
238 }
|
cannam@62
|
239 inline DynamicCapability::Client JsonCodec::decode(
|
cannam@62
|
240 kj::ArrayPtr<const char> input, InterfaceSchema type) const {
|
cannam@62
|
241 return decode(input, Type(type), Orphanage()).getReader().as<DynamicCapability>();
|
cannam@62
|
242 }
|
cannam@62
|
243 inline DynamicEnum JsonCodec::decode(kj::ArrayPtr<const char> input, EnumSchema type) const {
|
cannam@62
|
244 return decode(input, Type(type), Orphanage()).getReader().as<DynamicEnum>();
|
cannam@62
|
245 }
|
cannam@62
|
246
|
cannam@62
|
247 // -----------------------------------------------------------------------------
|
cannam@62
|
248
|
cannam@62
|
249 template <typename T>
|
cannam@62
|
250 void JsonCodec::encode(T&& value, JsonValue::Builder output) {
|
cannam@62
|
251 typedef FromAny<kj::Decay<T>> Base;
|
cannam@62
|
252 encode(DynamicValue::Reader(ReaderFor<Base>(kj::fwd<T>(value))), Type::from<Base>(), output);
|
cannam@62
|
253 }
|
cannam@62
|
254
|
cannam@62
|
255 template <typename T>
|
cannam@62
|
256 inline Orphan<T> JsonCodec::decode(JsonValue::Reader input, Orphanage orphanage) const {
|
cannam@62
|
257 return decode(input, Type::from<T>(), orphanage).template releaseAs<T>();
|
cannam@62
|
258 }
|
cannam@62
|
259
|
cannam@62
|
260 template <typename T>
|
cannam@62
|
261 inline ReaderFor<T> JsonCodec::decode(JsonValue::Reader input) const {
|
cannam@62
|
262 static_assert(style<T>() == Style::PRIMITIVE || style<T>() == Style::CAPABILITY,
|
cannam@62
|
263 "must specify an orphanage to decode an object type");
|
cannam@62
|
264 return decode(input, Type::from<T>(), Orphanage()).getReader().template as<T>();
|
cannam@62
|
265 }
|
cannam@62
|
266
|
cannam@62
|
267 inline Orphan<DynamicList> JsonCodec::decode(
|
cannam@62
|
268 JsonValue::Reader input, ListSchema type, Orphanage orphanage) const {
|
cannam@62
|
269 return decode(input, Type(type), orphanage).releaseAs<DynamicList>();
|
cannam@62
|
270 }
|
cannam@62
|
271 inline Orphan<DynamicStruct> JsonCodec::decode(
|
cannam@62
|
272 JsonValue::Reader input, StructSchema type, Orphanage orphanage) const {
|
cannam@62
|
273 return decode(input, Type(type), orphanage).releaseAs<DynamicStruct>();
|
cannam@62
|
274 }
|
cannam@62
|
275 inline DynamicCapability::Client JsonCodec::decode(
|
cannam@62
|
276 JsonValue::Reader input, InterfaceSchema type) const {
|
cannam@62
|
277 return decode(input, Type(type), Orphanage()).getReader().as<DynamicCapability>();
|
cannam@62
|
278 }
|
cannam@62
|
279 inline DynamicEnum JsonCodec::decode(JsonValue::Reader input, EnumSchema type) const {
|
cannam@62
|
280 return decode(input, Type(type), Orphanage()).getReader().as<DynamicEnum>();
|
cannam@62
|
281 }
|
cannam@62
|
282
|
cannam@62
|
283 // -----------------------------------------------------------------------------
|
cannam@62
|
284
|
cannam@62
|
285 class JsonCodec::HandlerBase {
|
cannam@62
|
286 // Internal helper; ignore.
|
cannam@62
|
287 public:
|
cannam@62
|
288 virtual void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
|
cannam@62
|
289 JsonValue::Builder output) const = 0;
|
cannam@62
|
290 virtual Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
291 Type type, Orphanage orphanage) const;
|
cannam@62
|
292 virtual void decodeStructBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
293 DynamicStruct::Builder output) const;
|
cannam@62
|
294 };
|
cannam@62
|
295
|
cannam@62
|
296 template <typename T>
|
cannam@62
|
297 class JsonCodec::Handler<T, Style::POINTER>: private JsonCodec::HandlerBase {
|
cannam@62
|
298 public:
|
cannam@62
|
299 virtual void encode(const JsonCodec& codec, ReaderFor<T> input,
|
cannam@62
|
300 JsonValue::Builder output) const = 0;
|
cannam@62
|
301 virtual Orphan<T> decode(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
302 Orphanage orphanage) const = 0;
|
cannam@62
|
303
|
cannam@62
|
304 private:
|
cannam@62
|
305 void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
|
cannam@62
|
306 JsonValue::Builder output) const override final {
|
cannam@62
|
307 encode(codec, input.as<T>(), output);
|
cannam@62
|
308 }
|
cannam@62
|
309 Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
310 Type type, Orphanage orphanage) const override final {
|
cannam@62
|
311 return decode(codec, input, orphanage);
|
cannam@62
|
312 }
|
cannam@62
|
313 friend class JsonCodec;
|
cannam@62
|
314 };
|
cannam@62
|
315
|
cannam@62
|
316 template <typename T>
|
cannam@62
|
317 class JsonCodec::Handler<T, Style::STRUCT>: private JsonCodec::HandlerBase {
|
cannam@62
|
318 public:
|
cannam@62
|
319 virtual void encode(const JsonCodec& codec, ReaderFor<T> input,
|
cannam@62
|
320 JsonValue::Builder output) const = 0;
|
cannam@62
|
321 virtual void decode(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
322 BuilderFor<T> output) const = 0;
|
cannam@62
|
323 virtual Orphan<T> decode(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
324 Orphanage orphanage) const {
|
cannam@62
|
325 // If subclass does not override, fall back to regular version.
|
cannam@62
|
326 auto result = orphanage.newOrphan<T>();
|
cannam@62
|
327 decode(codec, input, result.get());
|
cannam@62
|
328 return result;
|
cannam@62
|
329 }
|
cannam@62
|
330
|
cannam@62
|
331 private:
|
cannam@62
|
332 void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
|
cannam@62
|
333 JsonValue::Builder output) const override final {
|
cannam@62
|
334 encode(codec, input.as<T>(), output);
|
cannam@62
|
335 }
|
cannam@62
|
336 Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
337 Type type, Orphanage orphanage) const override final {
|
cannam@62
|
338 return decode(codec, input, orphanage);
|
cannam@62
|
339 }
|
cannam@62
|
340 void decodeStructBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
341 DynamicStruct::Builder output) const override final {
|
cannam@62
|
342 decode(codec, input, output.as<T>());
|
cannam@62
|
343 }
|
cannam@62
|
344 friend class JsonCodec;
|
cannam@62
|
345 };
|
cannam@62
|
346
|
cannam@62
|
347 template <>
|
cannam@62
|
348 class JsonCodec::Handler<DynamicStruct>: private JsonCodec::HandlerBase {
|
cannam@62
|
349 // Almost identical to Style::STRUCT except that we pass the struct type to decode().
|
cannam@62
|
350
|
cannam@62
|
351 public:
|
cannam@62
|
352 virtual void encode(const JsonCodec& codec, DynamicStruct::Reader input,
|
cannam@62
|
353 JsonValue::Builder output) const = 0;
|
cannam@62
|
354 virtual void decode(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
355 DynamicStruct::Builder output) const = 0;
|
cannam@62
|
356 virtual Orphan<DynamicStruct> decode(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
357 StructSchema type, Orphanage orphanage) const {
|
cannam@62
|
358 // If subclass does not override, fall back to regular version.
|
cannam@62
|
359 auto result = orphanage.newOrphan(type);
|
cannam@62
|
360 decode(codec, input, result.get());
|
cannam@62
|
361 return result;
|
cannam@62
|
362 }
|
cannam@62
|
363
|
cannam@62
|
364 private:
|
cannam@62
|
365 void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
|
cannam@62
|
366 JsonValue::Builder output) const override final {
|
cannam@62
|
367 encode(codec, input.as<DynamicStruct>(), output);
|
cannam@62
|
368 }
|
cannam@62
|
369 Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
370 Type type, Orphanage orphanage) const override final {
|
cannam@62
|
371 return decode(codec, input, type.asStruct(), orphanage);
|
cannam@62
|
372 }
|
cannam@62
|
373 void decodeStructBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
374 DynamicStruct::Builder output) const override final {
|
cannam@62
|
375 decode(codec, input, output.as<DynamicStruct>());
|
cannam@62
|
376 }
|
cannam@62
|
377 friend class JsonCodec;
|
cannam@62
|
378 };
|
cannam@62
|
379
|
cannam@62
|
380 template <typename T>
|
cannam@62
|
381 class JsonCodec::Handler<T, Style::PRIMITIVE>: private JsonCodec::HandlerBase {
|
cannam@62
|
382 public:
|
cannam@62
|
383 virtual void encode(const JsonCodec& codec, T input, JsonValue::Builder output) const = 0;
|
cannam@62
|
384 virtual T decode(const JsonCodec& codec, JsonValue::Reader input) const = 0;
|
cannam@62
|
385
|
cannam@62
|
386 private:
|
cannam@62
|
387 void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
|
cannam@62
|
388 JsonValue::Builder output) const override final {
|
cannam@62
|
389 encode(codec, input.as<T>(), output);
|
cannam@62
|
390 }
|
cannam@62
|
391 Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
392 Type type, Orphanage orphanage) const override final {
|
cannam@62
|
393 return decode(codec, input);
|
cannam@62
|
394 }
|
cannam@62
|
395 friend class JsonCodec;
|
cannam@62
|
396 };
|
cannam@62
|
397
|
cannam@62
|
398 template <typename T>
|
cannam@62
|
399 class JsonCodec::Handler<T, Style::CAPABILITY>: private JsonCodec::HandlerBase {
|
cannam@62
|
400 public:
|
cannam@62
|
401 virtual void encode(const JsonCodec& codec, typename T::Client input,
|
cannam@62
|
402 JsonValue::Builder output) const = 0;
|
cannam@62
|
403 virtual typename T::Client decode(const JsonCodec& codec, JsonValue::Reader input) const = 0;
|
cannam@62
|
404
|
cannam@62
|
405 private:
|
cannam@62
|
406 void encodeBase(const JsonCodec& codec, DynamicValue::Reader input,
|
cannam@62
|
407 JsonValue::Builder output) const override final {
|
cannam@62
|
408 encode(codec, input.as<T>(), output);
|
cannam@62
|
409 }
|
cannam@62
|
410 Orphan<DynamicValue> decodeBase(const JsonCodec& codec, JsonValue::Reader input,
|
cannam@62
|
411 Type type, Orphanage orphanage) const override final {
|
cannam@62
|
412 return orphanage.newOrphanCopy(decode(codec, input));
|
cannam@62
|
413 }
|
cannam@62
|
414 friend class JsonCodec;
|
cannam@62
|
415 };
|
cannam@62
|
416
|
cannam@62
|
417 template <typename T>
|
cannam@62
|
418 inline void JsonCodec::addTypeHandler(Handler<T>& handler) {
|
cannam@62
|
419 addTypeHandlerImpl(Type::from<T>(), handler);
|
cannam@62
|
420 }
|
cannam@62
|
421 inline void JsonCodec::addTypeHandler(Type type, Handler<DynamicValue>& handler) {
|
cannam@62
|
422 addTypeHandlerImpl(type, handler);
|
cannam@62
|
423 }
|
cannam@62
|
424 inline void JsonCodec::addTypeHandler(EnumSchema type, Handler<DynamicEnum>& handler) {
|
cannam@62
|
425 addTypeHandlerImpl(type, handler);
|
cannam@62
|
426 }
|
cannam@62
|
427 inline void JsonCodec::addTypeHandler(StructSchema type, Handler<DynamicStruct>& handler) {
|
cannam@62
|
428 addTypeHandlerImpl(type, handler);
|
cannam@62
|
429 }
|
cannam@62
|
430 inline void JsonCodec::addTypeHandler(ListSchema type, Handler<DynamicList>& handler) {
|
cannam@62
|
431 addTypeHandlerImpl(type, handler);
|
cannam@62
|
432 }
|
cannam@62
|
433 inline void JsonCodec::addTypeHandler(InterfaceSchema type, Handler<DynamicCapability>& handler) {
|
cannam@62
|
434 addTypeHandlerImpl(type, handler);
|
cannam@62
|
435 }
|
cannam@62
|
436
|
cannam@62
|
437 template <typename T>
|
cannam@62
|
438 inline void JsonCodec::addFieldHandler(StructSchema::Field field, Handler<T>& handler) {
|
cannam@62
|
439 addFieldHandlerImpl(field, Type::from<T>(), handler);
|
cannam@62
|
440 }
|
cannam@62
|
441
|
cannam@62
|
442 template <> void JsonCodec::addTypeHandler(Handler<DynamicValue>& handler)
|
cannam@62
|
443 KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
|
cannam@62
|
444 "try specifying a specific type schema as the first parameter");
|
cannam@62
|
445 template <> void JsonCodec::addTypeHandler(Handler<DynamicEnum>& handler)
|
cannam@62
|
446 KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
|
cannam@62
|
447 "try specifying a specific type schema as the first parameter");
|
cannam@62
|
448 template <> void JsonCodec::addTypeHandler(Handler<DynamicStruct>& handler)
|
cannam@62
|
449 KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
|
cannam@62
|
450 "try specifying a specific type schema as the first parameter");
|
cannam@62
|
451 template <> void JsonCodec::addTypeHandler(Handler<DynamicList>& handler)
|
cannam@62
|
452 KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
|
cannam@62
|
453 "try specifying a specific type schema as the first parameter");
|
cannam@62
|
454 template <> void JsonCodec::addTypeHandler(Handler<DynamicCapability>& handler)
|
cannam@62
|
455 KJ_UNAVAILABLE("JSON handlers for type sets (e.g. all structs, all lists) not implemented; "
|
cannam@62
|
456 "try specifying a specific type schema as the first parameter");
|
cannam@62
|
457 // TODO(someday): Implement support for registering handlers that cover thinsg like "all structs"
|
cannam@62
|
458 // or "all lists". Currently you can only target a specific struct or list type.
|
cannam@62
|
459
|
cannam@62
|
460 } // namespace capnp
|
cannam@62
|
461
|
cannam@62
|
462 #endif // CAPNP_COMPAT_JSON_H_
|