Commit e282e1b6 authored by Kenton Varda's avatar Kenton Varda

Add JSON annotations for encoding Data as base64 or hex.

parent 4311434a
...@@ -840,10 +840,14 @@ R"({ "names-can_contain!anything Really": "foo", ...@@ -840,10 +840,14 @@ R"({ "names-can_contain!anything Really": "foo",
"simpleGroup": {"renamed-grault": "garply"}, "simpleGroup": {"renamed-grault": "garply"},
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"], "enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
"innerJson": [123, "hello", {"object": true}], "innerJson": [123, "hello", {"object": true}],
"customFieldHandler": "add-prefix-waldo" })"_kj; "customFieldHandler": "add-prefix-waldo",
"testBase64": "ZnJlZA==",
"testHex": "706c756768" })"_kj;
static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE = static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE =
R"({ R"({
"testHex": "706c756768",
"testBase64": "ZnJlZA==",
"customFieldHandler": "add-prefix-waldo", "customFieldHandler": "add-prefix-waldo",
"innerJson": [123, "hello", {"object": true}], "innerJson": [123, "hello", {"object": true}],
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"], "enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
...@@ -927,6 +931,9 @@ KJ_TEST("rename fields") { ...@@ -927,6 +931,9 @@ KJ_TEST("rename fields") {
root.setCustomFieldHandler("waldo"); root.setCustomFieldHandler("waldo");
root.setTestBase64("fred"_kj.asBytes());
root.setTestHex("plugh"_kj.asBytes());
auto encoded = json.encode(root.asReader()); auto encoded = json.encode(root.asReader());
KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded); KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded);
......
...@@ -74,6 +74,9 @@ struct TestJsonAnnotations { ...@@ -74,6 +74,9 @@ struct TestJsonAnnotations {
innerJson @16 :Json.Value; innerJson @16 :Json.Value;
customFieldHandler @17 :Text; customFieldHandler @17 :Text;
testBase64 @18 :Data $Json.base64;
testHex @19 :Data $Json.hex;
} }
struct TestJsonAnnotations2 { struct TestJsonAnnotations2 {
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <kj/function.h> #include <kj/function.h>
#include <kj/vector.h> #include <kj/vector.h>
#include <kj/one-of.h> #include <kj/one-of.h>
#include <kj/encoding.h>
namespace capnp { namespace capnp {
...@@ -896,6 +897,32 @@ void JsonCodec::addFieldHandlerImpl(StructSchema::Field field, Type type, Handle ...@@ -896,6 +897,32 @@ void JsonCodec::addFieldHandlerImpl(StructSchema::Field field, Type type, Handle
static constexpr uint64_t JSON_NAME_ANNOTATION_ID = 0xfa5b1fd61c2e7c3dull; static constexpr uint64_t JSON_NAME_ANNOTATION_ID = 0xfa5b1fd61c2e7c3dull;
static constexpr uint64_t JSON_FLATTEN_ANNOTATION_ID = 0x82d3e852af0336bfull; static constexpr uint64_t JSON_FLATTEN_ANNOTATION_ID = 0x82d3e852af0336bfull;
static constexpr uint64_t JSON_DISCRIMINATOR_ANNOTATION_ID = 0xcfa794e8d19a0162ull; static constexpr uint64_t JSON_DISCRIMINATOR_ANNOTATION_ID = 0xcfa794e8d19a0162ull;
static constexpr uint64_t JSON_BASE64_ANNOTATION_ID = 0xd7d879450a253e4bull;
static constexpr uint64_t JSON_HEX_ANNOTATION_ID = 0xf061e22f0ae5c7b5ull;
class JsonCodec::Base64Handler final: public JsonCodec::Handler<capnp::Data> {
public:
void encode(const JsonCodec& codec, capnp::Data::Reader input, JsonValue::Builder output) const {
output.setString(kj::encodeBase64(input));
}
Orphan<capnp::Data> decode(const JsonCodec& codec, JsonValue::Reader input,
Orphanage orphanage) const {
return orphanage.newOrphanCopy(capnp::Data::Reader(kj::decodeBase64(input.getString())));
}
};
class JsonCodec::HexHandler final: public JsonCodec::Handler<capnp::Data> {
public:
void encode(const JsonCodec& codec, capnp::Data::Reader input, JsonValue::Builder output) const {
output.setString(kj::encodeHex(input));
}
Orphan<capnp::Data> decode(const JsonCodec& codec, JsonValue::Reader input,
Orphanage orphanage) const {
return orphanage.newOrphanCopy(capnp::Data::Reader(kj::decodeHex(input.getString())));
}
};
class JsonCodec::AnnotatedHandler final: public JsonCodec::Handler<DynamicStruct> { class JsonCodec::AnnotatedHandler final: public JsonCodec::Handler<DynamicStruct> {
public: public:
...@@ -962,6 +989,18 @@ public: ...@@ -962,6 +989,18 @@ public:
KJ_REQUIRE(fieldProto.isGroup(), "only unions can have discriminator"); KJ_REQUIRE(fieldProto.isGroup(), "only unions can have discriminator");
subDiscriminator = anno.getValue().getText(); subDiscriminator = anno.getValue().getText();
break; break;
case JSON_BASE64_ANNOTATION_ID: {
KJ_REQUIRE(field.getType().isData(), "only Data can be marked for base64 encoding");
static Base64Handler handler;
codec.addFieldHandler(field, handler);
break;
}
case JSON_HEX_ANNOTATION_ID: {
KJ_REQUIRE(field.getType().isData(), "only Data can be marked for hex encoding");
static HexHandler handler;
codec.addFieldHandler(field, handler);
break;
}
} }
} }
......
...@@ -92,3 +92,9 @@ annotation discriminator @0xcfa794e8d19a0162 (struct, union): Text; ...@@ -92,3 +92,9 @@ annotation discriminator @0xcfa794e8d19a0162 (struct, union): Text;
# by a special discriminator field with the given name. The value of the discriminator field is # by a special discriminator field with the given name. The value of the discriminator field is
# a string naming which variant is active. This allows the members of the union to have the # a string naming which variant is active. This allows the members of the union to have the
# $jsonFlatten annotation. # $jsonFlatten annotation.
annotation base64 @0xd7d879450a253e4b (field): Void;
# Place on a field of type `Data` to indicate that its JSON representation is a Base64 string.
annotation hex @0xf061e22f0ae5c7b5 (field): Void;
# Place on a field of type `Data` to indicate that its JSON representation is a hex string.
...@@ -231,6 +231,8 @@ private: ...@@ -231,6 +231,8 @@ private:
class HandlerBase; class HandlerBase;
class AnnotatedHandler; class AnnotatedHandler;
class AnnotatedEnumHandler; class AnnotatedEnumHandler;
class Base64Handler;
class HexHandler;
class JsonValueHandler; class JsonValueHandler;
struct Impl; struct Impl;
......
...@@ -13,4 +13,6 @@ ...@@ -13,4 +13,6 @@
"dependency": {"renamed-foo": "corge"}, "dependency": {"renamed-foo": "corge"},
"simpleGroup": {"renamed-grault": "garply"}, "simpleGroup": {"renamed-grault": "garply"},
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"], "enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
"innerJson": [123, "hello", {"object": true}] } "innerJson": [123, "hello", {"object": true}],
"testBase64": "ZnJlZA==",
"testHex": "706c756768" }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment