Unverified Commit 18b84c2d authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #721 from capnproto/json-annotations

More JSON annotation fixes/improvements
parents d76ba885 6b895a0c
...@@ -844,10 +844,14 @@ R"({ "names-can_contain!anything Really": "foo", ...@@ -844,10 +844,14 @@ R"({ "names-can_contain!anything Really": "foo",
"testBase64": "ZnJlZA==", "testBase64": "ZnJlZA==",
"testHex": "706c756768", "testHex": "706c756768",
"bUnion": "renamed-bar", "bUnion": "renamed-bar",
"bValue": 678 })"_kj; "bValue": 678,
"externalUnion": {"type": "bar", "value": "cba"},
"unionWithVoid": {"type": "voidValue"} })"_kj;
static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE = static constexpr kj::StringPtr GOLDEN_ANNOTATED_REVERSE =
R"({ R"({
"unionWithVoid": {"type": "voidValue"},
"externalUnion": {"type": "bar", "value": "cba"},
"bValue": 678, "bValue": 678,
"bUnion": "renamed-bar", "bUnion": "renamed-bar",
"testHex": "706c756768", "testHex": "706c756768",
...@@ -940,6 +944,10 @@ KJ_TEST("rename fields") { ...@@ -940,6 +944,10 @@ KJ_TEST("rename fields") {
root.getBUnion().setBar(678); root.getBUnion().setBar(678);
root.initExternalUnion().initBar().setValue("cba");
root.initUnionWithVoid().setVoidValue();
auto encoded = json.encode(root.asReader()); auto encoded = json.encode(root.asReader());
KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded); KJ_EXPECT(encoded == GOLDEN_ANNOTATED, encoded);
......
...@@ -82,6 +82,14 @@ struct TestJsonAnnotations { ...@@ -82,6 +82,14 @@ struct TestJsonAnnotations {
foo @20 :Text; foo @20 :Text;
bar @21 :UInt32 $Json.name("renamed-bar"); bar @21 :UInt32 $Json.name("renamed-bar");
} }
externalUnion @22 :TestJsonAnnotations3;
unionWithVoid :union $Json.discriminator(name = "type") {
intValue @23 :UInt32;
voidValue @24 :Void;
textValue @25 :Text;
}
} }
struct TestJsonAnnotations2 { struct TestJsonAnnotations2 {
...@@ -89,6 +97,17 @@ struct TestJsonAnnotations2 { ...@@ -89,6 +97,17 @@ struct TestJsonAnnotations2 {
cycle @1 :TestJsonAnnotations; cycle @1 :TestJsonAnnotations;
} }
struct TestJsonAnnotations3 $Json.discriminator(name = "type") {
union {
foo @0 :UInt32;
bar @1 :TestFlattenedStruct $Json.flatten();
}
}
struct TestFlattenedStruct {
value @0 :Text;
}
enum TestJsonAnnotatedEnum { enum TestJsonAnnotatedEnum {
foo @0; foo @0;
bar @1 $Json.name("renamed-bar"); bar @1 $Json.name("renamed-bar");
......
...@@ -860,13 +860,17 @@ void JsonCodec::HandlerBase::decodeStructBase( ...@@ -860,13 +860,17 @@ void JsonCodec::HandlerBase::decodeStructBase(
} }
void JsonCodec::addTypeHandlerImpl(Type type, HandlerBase& handler) { void JsonCodec::addTypeHandlerImpl(Type type, HandlerBase& handler) {
impl->typeHandlers.insert(type, &handler); impl->typeHandlers.upsert(type, &handler, [](HandlerBase*& existing, HandlerBase* replacement) {
KJ_REQUIRE(existing == replacement, "type already has a different registered handler");
});
} }
void JsonCodec::addFieldHandlerImpl(StructSchema::Field field, Type type, HandlerBase& handler) { void JsonCodec::addFieldHandlerImpl(StructSchema::Field field, Type type, HandlerBase& handler) {
KJ_REQUIRE(type == field.getType(), KJ_REQUIRE(type == field.getType(),
"handler type did not match field type for addFieldHandler()"); "handler type did not match field type for addFieldHandler()");
impl->fieldHandlers.insert(field, &handler); impl->fieldHandlers.upsert(field, &handler, [](HandlerBase*& existing, HandlerBase* replacement) {
KJ_REQUIRE(existing == replacement, "field already has a different registered handler");
});
} }
// ======================================================================================= // =======================================================================================
...@@ -1010,6 +1014,11 @@ public: ...@@ -1010,6 +1014,11 @@ public:
if (flattened) { if (flattened) {
info.flattenHandler = subHandler; info.flattenHandler = subHandler;
} }
} else if (type.isStruct()) {
if (flattened) {
info.flattenHandler = codec.loadAnnotatedHandler(
type.asStruct(), nullptr, nullptr, dependencies);
}
} }
bool isUnionMember = fieldProto.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT; bool isUnionMember = fieldProto.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT;
...@@ -1107,7 +1116,7 @@ public: ...@@ -1107,7 +1116,7 @@ public:
void decode(const JsonCodec& codec, JsonValue::Reader input, void decode(const JsonCodec& codec, JsonValue::Reader input,
DynamicStruct::Builder output) const override { DynamicStruct::Builder output) const override {
KJ_REQUIRE(input.isObject()); KJ_REQUIRE(input.isObject());
kj::HashMap<const void*, StructSchema::Field> unionsSeen; kj::HashSet<const void*> unionsSeen;
kj::Vector<JsonValue::Field::Reader> retries; kj::Vector<JsonValue::Field::Reader> retries;
for (auto field: input.getObject()) { for (auto field: input.getObject()) {
if (!decodeField(codec, field.getName(), field.getValue(), output, unionsSeen)) { if (!decodeField(codec, field.getName(), field.getValue(), output, unionsSeen)) {
...@@ -1235,15 +1244,19 @@ private: ...@@ -1235,15 +1244,19 @@ private:
KJ_IF_MAYBE(handler, info.flattenHandler) { KJ_IF_MAYBE(handler, info.flattenHandler) {
handler->gatherForEncode(codec, reader.get(*which), prefix, info.prefix, flattenedFields); handler->gatherForEncode(codec, reader.get(*which), prefix, info.prefix, flattenedFields);
} else { } else {
flattenedFields.add(FlattenedField { auto type = which->getType();
prefix, info.name, which->getType(), reader.get(*which) }); if (type.which() == schema::Type::VOID && unionTagName != nullptr) {
// When we have an explicit union discriminant, we don't need to encode void fields.
} else {
flattenedFields.add(FlattenedField {
prefix, info.name, which->getType(), reader.get(*which) });
}
} }
} }
} }
bool decodeField(const JsonCodec& codec, kj::StringPtr name, JsonValue::Reader value, bool decodeField(const JsonCodec& codec, kj::StringPtr name, JsonValue::Reader value,
DynamicStruct::Builder output, DynamicStruct::Builder output, kj::HashSet<const void*>& unionsSeen) const {
kj::HashMap<const void*, StructSchema::Field>& unionsSeen) const {
KJ_ASSERT(output.getSchema() == schema); KJ_ASSERT(output.getSchema() == schema);
KJ_IF_MAYBE(info, fieldsByName.find(name)) { KJ_IF_MAYBE(info, fieldsByName.find(name)) {
...@@ -1264,20 +1277,20 @@ private: ...@@ -1264,20 +1277,20 @@ private:
// Mark that we've seen a union tag for this struct. // Mark that we've seen a union tag for this struct.
const void* ptr = getUnionInstanceIdentifier(output); const void* ptr = getUnionInstanceIdentifier(output);
KJ_IF_MAYBE(field, unionTagValues.find(value.getString())) { KJ_IF_MAYBE(field, unionTagValues.find(value.getString())) {
unionsSeen.insert(ptr, *field); // clear() has the side-effect of activating this member of the union, without
// allocating any objects.
output.clear(*field);
unionsSeen.insert(ptr);
} }
return true; return true;
} }
case FieldNameInfo::FLATTENED_FROM_UNION: { case FieldNameInfo::FLATTENED_FROM_UNION: {
const void* ptr = getUnionInstanceIdentifier(output); const void* ptr = getUnionInstanceIdentifier(output);
KJ_IF_MAYBE(variant, unionsSeen.find(ptr)) { if (unionsSeen.contains(ptr)) {
bool alreadyInitialized = output.which() auto variant = KJ_ASSERT_NONNULL(output.which());
.map([&](auto f) { return f == *variant; }) return KJ_ASSERT_NONNULL(fields[variant.getIndex()].flattenHandler)
.orDefault(false);
auto child = alreadyInitialized ? output.get(*variant) : output.init(*variant);
return KJ_ASSERT_NONNULL(fields[variant->getIndex()].flattenHandler)
.decodeField(codec, name.slice(info->prefixLength), value, .decodeField(codec, name.slice(info->prefixLength), value,
child.as<DynamicStruct>(), unionsSeen); output.get(variant).as<DynamicStruct>(), unionsSeen);
} else { } else {
// We haven't seen the union tag yet, so we can't parse this field yet. Try again later. // We haven't seen the union tag yet, so we can't parse this field yet. Try again later.
return false; return false;
...@@ -1285,8 +1298,9 @@ private: ...@@ -1285,8 +1298,9 @@ private:
} }
case FieldNameInfo::UNION_VALUE: { case FieldNameInfo::UNION_VALUE: {
const void* ptr = getUnionInstanceIdentifier(output); const void* ptr = getUnionInstanceIdentifier(output);
KJ_IF_MAYBE(variant, unionsSeen.find(ptr)) { if (unionsSeen.contains(ptr)) {
codec.decodeField(*variant, value, Orphanage::getForMessageContaining(output), output); auto variant = KJ_ASSERT_NONNULL(output.which());
codec.decodeField(variant, value, Orphanage::getForMessageContaining(output), output);
return true; return true;
} else { } else {
// We haven't seen the union tag yet, so we can't parse this field yet. Try again later. // We haven't seen the union tag yet, so we can't parse this field yet. Try again later.
......
...@@ -17,4 +17,6 @@ ...@@ -17,4 +17,6 @@
"testBase64": "ZnJlZA==", "testBase64": "ZnJlZA==",
"testHex": "706c756768", "testHex": "706c756768",
"bUnion": "renamed-bar", "bUnion": "renamed-bar",
"bValue": 678 } "bValue": 678,
"externalUnion": {"type": "bar", "value": "cba"},
"unionWithVoid": {"type": "voidValue"} }
...@@ -44,8 +44,8 @@ public: ...@@ -44,8 +44,8 @@ public:
void reserve(size_t size); void reserve(size_t size);
// Pre-allocates space for a map of the given size. // Pre-allocates space for a map of the given size.
size_t size(); size_t size() const;
size_t capacity(); size_t capacity() const;
void clear(); void clear();
struct Entry { struct Entry {
...@@ -136,8 +136,8 @@ public: ...@@ -136,8 +136,8 @@ public:
void reserve(size_t size); void reserve(size_t size);
// Pre-allocates space for a map of the given size. // Pre-allocates space for a map of the given size.
size_t size(); size_t size() const;
size_t capacity(); size_t capacity() const;
void clear(); void clear();
struct Entry { struct Entry {
...@@ -262,6 +262,11 @@ class HashSet: public Table<Element, HashIndex<_::HashSetCallbacks>> { ...@@ -262,6 +262,11 @@ class HashSet: public Table<Element, HashIndex<_::HashSetCallbacks>> {
public: public:
// Everything is inherited. // Everything is inherited.
template <typename... Params>
inline bool contains(Params&&... params) const {
return this->find(kj::fwd<Params>(params)...) != nullptr;
}
}; };
template <typename Element> template <typename Element>
...@@ -281,11 +286,11 @@ void HashMap<Key, Value>::reserve(size_t size) { ...@@ -281,11 +286,11 @@ void HashMap<Key, Value>::reserve(size_t size) {
} }
template <typename Key, typename Value> template <typename Key, typename Value>
size_t HashMap<Key, Value>::size() { size_t HashMap<Key, Value>::size() const {
return table.size(); return table.size();
} }
template <typename Key, typename Value> template <typename Key, typename Value>
size_t HashMap<Key, Value>::capacity() { size_t HashMap<Key, Value>::capacity() const {
return table.capacity(); return table.capacity();
} }
template <typename Key, typename Value> template <typename Key, typename Value>
...@@ -373,11 +378,11 @@ void TreeMap<Key, Value>::reserve(size_t size) { ...@@ -373,11 +378,11 @@ void TreeMap<Key, Value>::reserve(size_t size) {
} }
template <typename Key, typename Value> template <typename Key, typename Value>
size_t TreeMap<Key, Value>::size() { size_t TreeMap<Key, Value>::size() const {
return table.size(); return table.size();
} }
template <typename Key, typename Value> template <typename Key, typename Value>
size_t TreeMap<Key, Value>::capacity() { size_t TreeMap<Key, Value>::capacity() const {
return table.capacity(); return table.capacity();
} }
template <typename Key, typename Value> template <typename Key, typename Value>
......
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