Commit 0a2d1e0c authored by Kenton Varda's avatar Kenton Varda

Support embedding JsonValue to represent arbitrary JSON.

parent 0aa08164
......@@ -838,10 +838,12 @@ R"({ "names-can_contain!anything Really": "foo",
"multiMember": "ghi",
"dependency": {"renamed-foo": "corge"},
"simpleGroup": {"renamed-grault": "garply"},
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"] })"_kj;
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
"innerJson": [123, "hello", {"object": true}] })"_kj;
static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE =
R"({
"innerJson": [123, "hello", {"object": true}],
"enums": ["qux", "renamed-bar", "foo", "renamed-baz"],
"simpleGroup": { "renamed-grault": "garply" },
"dependency": { "renamed-foo": "corge" },
......@@ -897,6 +899,14 @@ KJ_TEST("rename fields") {
TestJsonAnnotatedEnum::BAZ
});
auto val = root.initInnerJson();
auto arr = val.initArray(3);
arr[0].setNumber(123);
arr[1].setString("hello");
auto field = arr[2].initObject(1)[0];
field.setName("object");
field.initValue().setBoolean(true);
auto encoded = json.encode(root.asReader());
KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded);
......
......@@ -70,6 +70,8 @@ struct TestJsonAnnotations {
}
enums @15 :List(TestJsonAnnotatedEnum);
innerJson @16 :Json.Value;
}
struct TestJsonAnnotations2 {
......
......@@ -1270,6 +1270,42 @@ private:
std::map<kj::StringPtr, uint16_t> nameToValue;
};
class JsonCodec::JsonValueHandler final: public JsonCodec::Handler<DynamicStruct> {
public:
void encode(const JsonCodec& codec, DynamicStruct::Reader input,
JsonValue::Builder output) const override {
rawCopy(input, kj::mv(output));
}
void decode(const JsonCodec& codec, JsonValue::Reader input,
DynamicStruct::Builder output) const override {
rawCopy(input, kj::mv(output));
}
private:
EnumSchema schema;
kj::Array<kj::StringPtr> valueToName;
std::map<kj::StringPtr, uint16_t> nameToValue;
void rawCopy(AnyStruct::Reader input, AnyStruct::Builder output) const {
// HACK: Manually copy using AnyStruct, so that if JsonValue's definition changes, this code
// doesn't need to be updated. However, note that if JsonValue ever adds new fields that
// change its size, and the input struct is a newer version than the output, we may lose
// the new fields. Technically the "correct" thing to do would be to allocate the output
// struct to be exactly the same size as the input, but JsonCodec's Handler interface is
// not designed to allow that -- it passes in an already-allocated builder. Oops.
auto dataIn = input.getDataSection();
auto dataOut = output.getDataSection();
memcpy(dataOut.begin(), dataIn.begin(), kj::min(dataOut.size(), dataIn.size()));
auto ptrIn = input.getPointerSection();
auto ptrOut = output.getPointerSection();
for (auto i: kj::zeroTo(kj::min(ptrIn.size(), ptrOut.size()))) {
ptrOut[i].set(ptrIn[i]);
}
}
};
JsonCodec::AnnotatedHandler& JsonCodec::loadAnnotatedHandler(
StructSchema schema, kj::Maybe<kj::StringPtr> discriminator,
kj::Vector<Schema>& dependencies) {
......@@ -1291,10 +1327,16 @@ JsonCodec::AnnotatedHandler& JsonCodec::loadAnnotatedHandler(
void JsonCodec::handleByAnnotation(Schema schema) {
switch (schema.getProto().which()) {
case schema::Node::STRUCT: {
kj::Vector<Schema> dependencies;
loadAnnotatedHandler(schema.asStruct(), nullptr, dependencies);
for (auto dep: dependencies) {
handleByAnnotation(dep);
if (schema.getProto().getId() == capnp::typeId<JsonValue>()) {
// Special handler for JsonValue.
static JsonValueHandler GLOBAL_HANDLER;
addTypeHandler(schema.asStruct(), GLOBAL_HANDLER);
} else {
kj::Vector<Schema> dependencies;
loadAnnotatedHandler(schema.asStruct(), nullptr, dependencies);
for (auto dep: dependencies) {
handleByAnnotation(dep);
}
}
break;
}
......
......@@ -78,6 +78,9 @@ annotation flatten @0x82d3e852af0336bf (field, group, union): FlattenOptions;
#
# In order to flatten a member of a union, the union (or, for an anonymous union, the parent
# struct type) must have the $jsonDiscribinator annotation.
#
# TODO(someday): Maybe support "flattening" a List(Value.Field) as a way to support unknown JSON
# fields?
struct FlattenOptions {
prefix @0 :Text = "";
......
......@@ -231,6 +231,7 @@ private:
class HandlerBase;
class AnnotatedHandler;
class AnnotatedEnumHandler;
class JsonValueHandler;
struct Impl;
kj::Own<Impl> impl;
......
......@@ -262,7 +262,7 @@ public:
template <typename T, typename = kj::EnableIf<kind<FromBuilder<T>>() == Kind::STRUCT>>
inline Builder(T&& value): Builder(toDynamic(value)) {}
inline operator AnyStruct::Reader() { return AnyStruct::Builder(builder); }
inline operator AnyStruct::Builder() { return AnyStruct::Builder(builder); }
inline MessageSize totalSize() const { return asReader().totalSize(); }
......
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