From fb585045692c482b6946fff63f0cd8425c8c70b5 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 2 Feb 2015 13:38:05 -0800 Subject: Support maps in JSON parsing and serialization. This is a sync of our internal developing of JSON parsing and serialization. It implements native understanding of MapEntry submessages, so that map fields with (key, value) pairs are serialized as JSON maps (objects) natively rather than as arrays of objects with 'key' and 'value' fields. The parser also now understands how to emit handler calls corresponding to MapEntry objects when processing a map field. This sync also picks up a bugfix in `table.c` to handle an alloc-failed case. --- tests/json/test_json.cc | 98 ++++++++++++++++++++++++++++++++++++++++++++++++- tests/test_def.c | 34 +++++++++++++++++ 2 files changed, 130 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/json/test_json.cc b/tests/json/test_json.cc index f1e2304..4465c76 100644 --- a/tests/json/test_json.cc +++ b/tests/json/test_json.cc @@ -85,6 +85,33 @@ static TestCase kTestRoundtripMessages[] = { TEST("{\"optional_string\":\"\\uFFFF\"}"), EXPECT("{\"optional_string\":\"\xEF\xBF\xBF\"}") }, + // map-field tests + { + TEST("{\"map_string_string\":{\"a\":\"value1\",\"b\":\"value2\"," + "\"c\":\"value3\"}}"), + EXPECT_SAME + }, + { + TEST("{\"map_int32_string\":{\"1\":\"value1\",\"-1\":\"value2\"," + "\"1234\":\"value3\"}}"), + EXPECT_SAME + }, + { + TEST("{\"map_bool_string\":{\"false\":\"value1\",\"true\":\"value2\"}}"), + EXPECT_SAME + }, + { + TEST("{\"map_string_int32\":{\"asdf\":1234,\"jkl;\":-1}}"), + EXPECT_SAME + }, + { + TEST("{\"map_string_bool\":{\"asdf\":true,\"jkl;\":false}}"), + EXPECT_SAME + }, + { + TEST("{\"map_string_msg\":{\"asdf\":{\"foo\":42},\"jkl;\":{\"foo\":84}}}"), + EXPECT_SAME + }, TEST_SENTINEL }; @@ -115,6 +142,53 @@ static const upb::MessageDef* BuildTestMessage( submsg->set_full_name("SubMessage", &st); AddField(submsg.get(), 1, "foo", UPB_TYPE_INT32, false); + // Create MapEntryStringString. + upb::reffed_ptr mapentry_string_string( + upb::MessageDef::New()); + mapentry_string_string->set_full_name("MapEntry_String_String", &st); + mapentry_string_string->setmapentry(true); + AddField(mapentry_string_string.get(), 1, "key", UPB_TYPE_STRING, false); + AddField(mapentry_string_string.get(), 2, "value", UPB_TYPE_STRING, false); + + // Create MapEntryInt32String. + upb::reffed_ptr mapentry_int32_string( + upb::MessageDef::New()); + mapentry_int32_string->set_full_name("MapEntry_Int32_String", &st); + mapentry_int32_string->setmapentry(true); + AddField(mapentry_int32_string.get(), 1, "key", UPB_TYPE_INT32, false); + AddField(mapentry_int32_string.get(), 2, "value", UPB_TYPE_STRING, false); + + // Create MapEntryBoolString. + upb::reffed_ptr mapentry_bool_string( + upb::MessageDef::New()); + mapentry_bool_string->set_full_name("MapEntry_Bool_String", &st); + mapentry_bool_string->setmapentry(true); + AddField(mapentry_bool_string.get(), 1, "key", UPB_TYPE_BOOL, false); + AddField(mapentry_bool_string.get(), 2, "value", UPB_TYPE_STRING, false); + + // Create MapEntryStringInt32. + upb::reffed_ptr mapentry_string_int32( + upb::MessageDef::New()); + mapentry_string_int32->set_full_name("MapEntry_String_Int32", &st); + mapentry_string_int32->setmapentry(true); + AddField(mapentry_string_int32.get(), 1, "key", UPB_TYPE_STRING, false); + AddField(mapentry_string_int32.get(), 2, "value", UPB_TYPE_INT32, false); + + // Create MapEntryStringBool. + upb::reffed_ptr mapentry_string_bool(upb::MessageDef::New()); + mapentry_string_bool->set_full_name("MapEntry_String_Bool", &st); + mapentry_string_bool->setmapentry(true); + AddField(mapentry_string_bool.get(), 1, "key", UPB_TYPE_STRING, false); + AddField(mapentry_string_bool.get(), 2, "value", UPB_TYPE_BOOL, false); + + // Create MapEntryStringMessage. + upb::reffed_ptr mapentry_string_msg(upb::MessageDef::New()); + mapentry_string_msg->set_full_name("MapEntry_String_Message", &st); + mapentry_string_msg->setmapentry(true); + AddField(mapentry_string_msg.get(), 1, "key", UPB_TYPE_STRING, false); + AddField(mapentry_string_msg.get(), 2, "value", UPB_TYPE_MESSAGE, false, + upb::upcast(submsg.get())); + // Create MyEnum. upb::reffed_ptr myenum(upb::EnumDef::New()); myenum->set_full_name("MyEnum", &st); @@ -150,13 +224,33 @@ static const upb::MessageDef* BuildTestMessage( AddField(md.get(), 19, "optional_enum", UPB_TYPE_ENUM, true, upb::upcast(myenum.get())); + AddField(md.get(), 20, "map_string_string", UPB_TYPE_MESSAGE, true, + upb::upcast(mapentry_string_string.get())); + AddField(md.get(), 21, "map_int32_string", UPB_TYPE_MESSAGE, true, + upb::upcast(mapentry_int32_string.get())); + AddField(md.get(), 22, "map_bool_string", UPB_TYPE_MESSAGE, true, + upb::upcast(mapentry_bool_string.get())); + AddField(md.get(), 23, "map_string_int32", UPB_TYPE_MESSAGE, true, + upb::upcast(mapentry_string_int32.get())); + AddField(md.get(), 24, "map_string_bool", UPB_TYPE_MESSAGE, true, + upb::upcast(mapentry_string_bool.get())); + AddField(md.get(), 25, "map_string_msg", UPB_TYPE_MESSAGE, true, + upb::upcast(mapentry_string_msg.get())); + // Add both to our symtab. - upb::Def* defs[3] = { + upb::Def* defs[9] = { upb::upcast(submsg.ReleaseTo(&defs)), upb::upcast(myenum.ReleaseTo(&defs)), upb::upcast(md.ReleaseTo(&defs)), + upb::upcast(mapentry_string_string.ReleaseTo(&defs)), + upb::upcast(mapentry_int32_string.ReleaseTo(&defs)), + upb::upcast(mapentry_bool_string.ReleaseTo(&defs)), + upb::upcast(mapentry_string_int32.ReleaseTo(&defs)), + upb::upcast(mapentry_string_bool.ReleaseTo(&defs)), + upb::upcast(mapentry_string_msg.ReleaseTo(&defs)), }; - symtab->Add(defs, 3, &defs, &st); + symtab->Add(defs, 9, &defs, &st); + ASSERT(st.ok()); // Return TestMessage. return symtab->LookupMessage("TestMessage"); diff --git a/tests/test_def.c b/tests/test_def.c index b85b031..800d685 100644 --- a/tests/test_def.c +++ b/tests/test_def.c @@ -344,6 +344,39 @@ static void test_descriptor_flags() { upb_msgdef_unref(m2, &m2); } +static void test_mapentry_check() { + upb_status s = UPB_STATUS_INIT; + + upb_msgdef *m = upb_msgdef_new(&m); + upb_msgdef_setfullname(m, "TestMessage", &s); + upb_fielddef *f = upb_fielddef_new(&f); + upb_fielddef_setname(f, "field1", &s); + upb_fielddef_setnumber(f, 1, &s); + upb_fielddef_setlabel(f, UPB_LABEL_OPTIONAL); + upb_fielddef_settype(f, UPB_TYPE_MESSAGE); + upb_fielddef_setsubdefname(f, ".MapEntry", &s); + upb_msgdef_addfield(m, f, &f, &s); + ASSERT(upb_ok(&s)); + + upb_msgdef *subm = upb_msgdef_new(&subm); + upb_msgdef_setfullname(subm, "MapEntry", &s); + upb_msgdef_setmapentry(subm, true); + + upb_symtab *symtab = upb_symtab_new(&symtab); + upb_def *defs[] = {UPB_UPCAST(m), UPB_UPCAST(subm)}; + upb_symtab_add(symtab, defs, 2, NULL, &s); + // Should not have succeeded: non-repeated field pointing to a MapEntry. + ASSERT(!upb_ok(&s)); + + upb_fielddef_setlabel(f, UPB_LABEL_REPEATED); + upb_symtab_add(symtab, defs, 2, NULL, &s); + ASSERT(upb_ok(&s)); + + upb_symtab_unref(symtab, &symtab); + upb_msgdef_unref(subm, &subm); + upb_msgdef_unref(m, &m); +} + static void test_oneofs() { upb_status s = UPB_STATUS_INIT; bool ok = true; @@ -412,6 +445,7 @@ int run_tests(int argc, char *argv[]) { test_partial_freeze(); test_noreftracking(); test_descriptor_flags(); + test_mapentry_check(); test_oneofs(); return 0; } -- cgit v1.2.3