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