Updated to a new version of protobuf and fixed a few conformance tests. When `google.protobuf.NullValue` appears on its own, we need to parse and serialize it as plain JSON "null" value.
diff --git a/upb/json_decode.c b/upb/json_decode.c index 953d238..c0aabd8 100644 --- a/upb/json_decode.c +++ b/upb/json_decode.c
@@ -42,6 +42,19 @@ return str.size == strlen(lit) && memcmp(str.data, lit, str.size) == 0; } +static bool jsondec_isnullvalue(const upb_fielddef *f) { + return upb_fielddef_type(f) == UPB_TYPE_ENUM && + strcmp(upb_enumdef_fullname(upb_fielddef_enumsubdef(f)), + "google.protobuf.NullValue") == 0; +} + +static bool jsondec_isvalue(const upb_fielddef *f) { + return (upb_fielddef_type(f) == UPB_TYPE_MESSAGE && + upb_msgdef_wellknowntype(upb_fielddef_msgsubdef(f)) == + UPB_WELLKNOWN_VALUE) || + jsondec_isnullvalue(f); +} + UPB_NORETURN static void jsondec_err(jsondec *d, const char *msg) { upb_status_seterrf(d->status, "Error parsing JSON @%d:%d: %s", d->line, (int)(d->ptr - d->line_begin), msg); @@ -769,21 +782,30 @@ } static upb_msgval jsondec_enum(jsondec *d, const upb_fielddef *f) { - if (jsondec_peek(d) == JD_STRING) { - const upb_enumdef *e = upb_fielddef_enumsubdef(f); - upb_strview str = jsondec_string(d); - upb_msgval val; - if (!upb_enumdef_ntoi(e, str.data, str.size, &val.int32_val)) { - if (d->options & UPB_JSONDEC_IGNOREUNKNOWN) { - val.int32_val = 0; - } else { - jsondec_errf(d, "Unknown enumerator: '" UPB_STRVIEW_FORMAT "'", - UPB_STRVIEW_ARGS(str)); + switch (jsondec_peek(d)) { + case JD_STRING: { + const upb_enumdef *e = upb_fielddef_enumsubdef(f); + upb_strview str = jsondec_string(d); + upb_msgval val; + if (!upb_enumdef_ntoi(e, str.data, str.size, &val.int32_val)) { + if (d->options & UPB_JSONDEC_IGNOREUNKNOWN) { + val.int32_val = 0; + } else { + jsondec_errf(d, "Unknown enumerator: '" UPB_STRVIEW_FORMAT "'", + UPB_STRVIEW_ARGS(str)); + } } + return val; } - return val; - } else { - return jsondec_int(d, f); + case JD_NULL: { + UPB_ASSERT(jsondec_isnullvalue(f)); + jsondec_null(d); + upb_msgval val; + val.int32_val = 0; + return val; + } + default: + return jsondec_int(d, f); } } @@ -867,12 +889,6 @@ return val; } -static bool jsondec_isvalue(const upb_fielddef *f) { - return upb_fielddef_type(f) == UPB_TYPE_MESSAGE && - upb_msgdef_wellknowntype(upb_fielddef_msgsubdef(f)) == - UPB_WELLKNOWN_VALUE; -} - static void jsondec_field(jsondec *d, upb_msg *msg, const upb_msgdef *m) { upb_strview name; const upb_fielddef *f;
diff --git a/upb/json_encode.c b/upb/json_encode.c index 8338b4d..587772d 100644 --- a/upb/json_encode.c +++ b/upb/json_encode.c
@@ -167,12 +167,17 @@ static void jsonenc_enum(int32_t val, const upb_fielddef *f, jsonenc *e) { const upb_enumdef *e_def = upb_fielddef_enumsubdef(f); - const char *name = upb_enumdef_iton(e_def, val); - if (name) { - jsonenc_printf(e, "\"%s\"", name); + if (strcmp(upb_enumdef_fullname(e_def), "google.protobuf.NullValue") == 0) { + jsonenc_putstr(e, "null"); } else { - jsonenc_printf(e, "%" PRId32, val); + const char *name = upb_enumdef_iton(e_def, val); + + if (name) { + jsonenc_printf(e, "\"%s\"", name); + } else { + jsonenc_printf(e, "%" PRId32, val); + } } }