Commit 4311434a authored by Kenton Varda's avatar Kenton Varda

Properly support custom field handlers together with annotated JSON.

parent 29a2e875
......@@ -839,10 +839,12 @@ R"({ "names-can_contain!anything Really": "foo",
"dependency": {"renamed-foo": "corge"},
"simpleGroup": {"renamed-grault": "garply"},
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
"innerJson": [123, "hello", {"object": true}] })"_kj;
"innerJson": [123, "hello", {"object": true}],
"customFieldHandler": "add-prefix-waldo" })"_kj;
static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE =
R"({
"customFieldHandler": "add-prefix-waldo",
"innerJson": [123, "hello", {"object": true}],
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
"simpleGroup": { "renamed-grault": "garply" },
......@@ -861,11 +863,27 @@ R"({
"names-can_contain!anything Really": "foo"
})"_kj;
class PrefixAdder: public JsonCodec::Handler<capnp::Text> {
public:
void encode(const JsonCodec& codec, capnp::Text::Reader input, JsonValue::Builder output) const {
output.setString(kj::str("add-prefix-", input));
}
Orphan<capnp::Text> decode(const JsonCodec& codec, JsonValue::Reader input,
Orphanage orphanage) const {
return orphanage.newOrphanCopy(capnp::Text::Reader(input.getString().slice(11)));
}
};
KJ_TEST("rename fields") {
JsonCodec json;
json.handleByAnnotation<TestJsonAnnotations>();
json.setPrettyPrint(true);
PrefixAdder customHandler;
json.addFieldHandler(Schema::from<TestJsonAnnotations>().getFieldByName("customFieldHandler"),
customHandler);
kj::String goldenText;
{
......@@ -907,6 +925,8 @@ KJ_TEST("rename fields") {
field.setName("object");
field.initValue().setBoolean(true);
root.setCustomFieldHandler("waldo");
auto encoded = json.encode(root.asReader());
KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded);
......
......@@ -72,6 +72,8 @@ struct TestJsonAnnotations {
enums @15 :List(TestJsonAnnotatedEnum);
innerJson @16 :Json.Value;
customFieldHandler @17 :Text;
}
struct TestJsonAnnotations2 {
......
......@@ -30,6 +30,7 @@
#include <kj/debug.h>
#include <kj/function.h>
#include <kj/vector.h>
#include <kj/one-of.h>
namespace capnp {
......@@ -403,21 +404,25 @@ void JsonCodec::decodeObject(JsonValue::Reader input, StructSchema type, Orphana
KJ_REQUIRE(input.isObject(), "Expected object value");
for (auto field: input.getObject()) {
KJ_IF_MAYBE(fieldSchema, type.findFieldByName(field.getName())) {
auto fieldValue = field.getValue();
auto fieldType = (*fieldSchema).getType();
auto iter = impl->fieldHandlers.find(*fieldSchema);
if (iter != impl->fieldHandlers.end()) {
output.adopt(*fieldSchema, iter->second->decodeBase(*this, fieldValue, fieldType, orphanage));
} else {
output.adopt(*fieldSchema, decode(fieldValue, fieldType, orphanage));
}
decodeField(*fieldSchema, field.getValue(), orphanage, output);
} else {
// Unknown json fields are ignored to allow schema evolution
}
}
}
void JsonCodec::decodeField(StructSchema::Field fieldSchema, JsonValue::Reader fieldValue,
Orphanage orphanage, DynamicStruct::Builder output) const {
auto fieldType = fieldSchema.getType();
auto iter = impl->fieldHandlers.find(fieldSchema);
if (iter != impl->fieldHandlers.end()) {
output.adopt(fieldSchema, iter->second->decodeBase(*this, fieldValue, fieldType, orphanage));
} else {
output.adopt(fieldSchema, decode(fieldValue, fieldType, orphanage));
}
}
void JsonCodec::decode(JsonValue::Reader input, DynamicStruct::Builder output) const {
auto type = output.getSchema();
......@@ -1027,7 +1032,14 @@ public:
auto& in = flattenedFields[i];
auto out = outs[i];
out.setName(in.name);
codec.encode(in.value, in.type, out.initValue());
KJ_SWITCH_ONEOF(in.type) {
KJ_CASE_ONEOF(type, Type) {
codec.encode(in.value, type, out.initValue());
}
KJ_CASE_ONEOF(field, StructSchema::Field) {
codec.encodeField(field, in.value, out.initValue());
}
}
}
}
......@@ -1111,10 +1123,11 @@ private:
struct FlattenedField {
kj::String ownName;
kj::StringPtr name;
Type type;
kj::OneOf<StructSchema::Field, Type> type;
DynamicValue::Reader value;
FlattenedField(kj::StringPtr prefix, kj::StringPtr name, Type type, DynamicValue::Reader value)
FlattenedField(kj::StringPtr prefix, kj::StringPtr name,
kj::OneOf<StructSchema::Field, Type> type, DynamicValue::Reader value)
: ownName(prefix.size() > 0 ? kj::str(prefix, name) : nullptr),
name(prefix.size() > 0 ? ownName : name),
type(type), value(value) {}
......@@ -1143,7 +1156,7 @@ private:
handler->gatherForEncode(codec, reader.get(field), prefix, info.prefix, flattenedFields);
} else {
flattenedFields.add(FlattenedField {
prefix, info.name, field.getType(), reader.get(field) });
prefix, info.name, field, reader.get(field) });
}
}
......@@ -1151,7 +1164,7 @@ private:
auto& info = fields[which->getIndex()];
KJ_IF_MAYBE(tag, unionTagName) {
flattenedFields.add(FlattenedField {
prefix, *tag, schema::Type::TEXT, Text::Reader(info.name) });
prefix, *tag, Type(schema::Type::TEXT), Text::Reader(info.name) });
}
KJ_IF_MAYBE(handler, info.flattenHandler) {
......@@ -1175,8 +1188,7 @@ private:
switch (info.type) {
case FieldNameInfo::NORMAL: {
auto field = output.getSchema().getFields()[info.index];
output.adopt(field, codec.decode(value, field.getType(),
Orphanage::getForMessageContaining(output)));
codec.decodeField(field, value, Orphanage::getForMessageContaining(output), output);
return true;
}
case FieldNameInfo::FLATTENED:
......
......@@ -240,6 +240,8 @@ private:
JsonValue::Builder output) const;
Orphan<DynamicList> decodeArray(List<JsonValue>::Reader input, ListSchema type, Orphanage orphanage) const;
void decodeObject(JsonValue::Reader input, StructSchema type, Orphanage orphanage, DynamicStruct::Builder output) const;
void decodeField(StructSchema::Field fieldSchema, JsonValue::Reader fieldValue,
Orphanage orphanage, DynamicStruct::Builder output) const;
void addTypeHandlerImpl(Type type, HandlerBase& handler);
void addFieldHandlerImpl(StructSchema::Field field, Type type, HandlerBase& handler);
......
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