Add new 'always_print_without_presence_fields' option to the C++ JSON serializer.

This flag has consistent behavior between proto2 and proto3 optionals (by not including either one), unlike always_print_primitive_fields which does include proto2 optional but excludes proto3 optionals.

always_print_primitive_fields is now deprecated and will be removed in an upcoming release.

PiperOrigin-RevId: 603362526
pull/15674/head
Protobuf Team Bot 10 months ago committed by Copybara-Service
parent 4687ef335f
commit 671b61b523
  1. 8
      src/google/protobuf/json/internal/unparser.cc
  2. 18
      src/google/protobuf/json/internal/writer.h
  3. 4
      src/google/protobuf/json/json.cc
  4. 18
      src/google/protobuf/json/json.h
  5. 166
      src/google/protobuf/json/json_test.cc

@ -433,8 +433,9 @@ absl::Status WriteField(JsonWriter& writer, const Msg<Traits>& msg,
} else if (Traits::IsRepeated(field)) { } else if (Traits::IsRepeated(field)) {
return WriteRepeated<Traits>(writer, msg, field); return WriteRepeated<Traits>(writer, msg, field);
} else if (Traits::GetSize(field, msg) == 0) { } else if (Traits::GetSize(field, msg) == 0) {
// We can only get here if always_print_primitive_fields is true. // We can only get here if one of the always_print options is true.
ABSL_DCHECK(writer.options().always_print_primitive_fields); ABSL_DCHECK(writer.options().always_print_primitive_fields ||
writer.options().always_print_without_presence_fields);
if (Traits::FieldType(field) == FieldDescriptor::TYPE_GROUP) { if (Traits::FieldType(field) == FieldDescriptor::TYPE_GROUP) {
// We do not yet have full group support, but this is required so that we // We do not yet have full group support, but this is required so that we
@ -464,6 +465,9 @@ absl::Status WriteFields(JsonWriter& writer, const Msg<Traits>& msg,
Traits::FieldType(field) == FieldDescriptor::TYPE_MESSAGE; Traits::FieldType(field) == FieldDescriptor::TYPE_MESSAGE;
has |= !is_singular_message && !Traits::IsOneof(field); has |= !is_singular_message && !Traits::IsOneof(field);
} }
if (writer.options().always_print_without_presence_fields) {
has |= Traits::IsRepeated(field) || Traits::IsImplicitPresence(field);
}
if (has) { if (has) {
fields.push_back(field); fields.push_back(field);

@ -36,11 +36,21 @@ struct WriterOptions {
// Whether to add spaces, line breaks and indentation to make the JSON output // Whether to add spaces, line breaks and indentation to make the JSON output
// easy to read. // easy to read.
bool add_whitespace = false; bool add_whitespace = false;
// Whether to always print primitive fields. By default proto3 primitive // Whether to always print the following types of fields even if they would
// fields with default values will be omitted in JSON output. For example, an // otherwise be omitted:
// int32 field set to 0 will be omitted. Set this flag to true will override // - Implicit presence fields set to their 0 value
// the default behavior and print primitive fields regardless of their values. // - Empty lists and maps
// - Proto2 optional and required scalar fields which are not present (but not
// Proto3 optional scalar fields).
// Note: This option is deprecated in favor of
// always_print_without_presence_fields which treats proto2 and proto3
// optionals the same and will be removed in an upcoming release.
bool always_print_primitive_fields = false; bool always_print_primitive_fields = false;
// Whether to always print fields which do not support presence if they would
// otherwise be omitted, namely:
// - Implicit presence fields set to their 0 value
// - Empty lists and maps
bool always_print_without_presence_fields = false;
// Whether to always print enums as ints. By default they are rendered as // Whether to always print enums as ints. By default they are rendered as
// strings. // strings.
bool always_print_enums_as_ints = false; bool always_print_enums_as_ints = false;

@ -34,6 +34,8 @@ absl::Status BinaryToJsonStream(google::protobuf::util::TypeResolver* resolver,
opts.preserve_proto_field_names = options.preserve_proto_field_names; opts.preserve_proto_field_names = options.preserve_proto_field_names;
opts.always_print_enums_as_ints = options.always_print_enums_as_ints; opts.always_print_enums_as_ints = options.always_print_enums_as_ints;
opts.always_print_primitive_fields = options.always_print_primitive_fields; opts.always_print_primitive_fields = options.always_print_primitive_fields;
opts.always_print_without_presence_fields =
options.always_print_without_presence_fields;
opts.unquote_int64_if_possible = options.unquote_int64_if_possible; opts.unquote_int64_if_possible = options.unquote_int64_if_possible;
// TODO: Drop this setting. // TODO: Drop this setting.
@ -88,6 +90,8 @@ absl::Status MessageToJsonString(const Message& message, std::string* output,
opts.preserve_proto_field_names = options.preserve_proto_field_names; opts.preserve_proto_field_names = options.preserve_proto_field_names;
opts.always_print_enums_as_ints = options.always_print_enums_as_ints; opts.always_print_enums_as_ints = options.always_print_enums_as_ints;
opts.always_print_primitive_fields = options.always_print_primitive_fields; opts.always_print_primitive_fields = options.always_print_primitive_fields;
opts.always_print_without_presence_fields =
options.always_print_without_presence_fields;
opts.unquote_int64_if_possible = options.unquote_int64_if_possible; opts.unquote_int64_if_possible = options.unquote_int64_if_possible;
// TODO: Drop this setting. // TODO: Drop this setting.

@ -39,11 +39,21 @@ struct PrintOptions {
// Whether to add spaces, line breaks and indentation to make the JSON output // Whether to add spaces, line breaks and indentation to make the JSON output
// easy to read. // easy to read.
bool add_whitespace = false; bool add_whitespace = false;
// Whether to always print primitive fields. By default proto3 primitive // Whether to always print the following types of fields even if they would
// fields with default values will be omitted in JSON output. For example, an // otherwise be omitted:
// int32 field set to 0 will be omitted. Set this flag to true will override // - Implicit presence fields set to their 0 value
// the default behavior and print primitive fields regardless of their values. // - Empty lists and maps
// - Proto2 optional and required scalar fields which are not present (but not
// Proto3 optional scalar fields).
// Note: This option is deprecated in favor of
// always_print_without_presence_fields which treats proto2 and proto3
// optionals the same and will be removed in an upcoming release.
bool always_print_primitive_fields = false; bool always_print_primitive_fields = false;
// Whether to always print fields which do not support presence if they would
// otherwise be omitted, namely:
// - Implicit presence fields set to their 0 value
// - Empty lists and maps
bool always_print_without_presence_fields = false;
// Whether to always print enums as ints. By default they are rendered as // Whether to always print enums as ints. By default they are rendered as
// strings. // strings.
bool always_print_enums_as_ints = false; bool always_print_enums_as_ints = false;

@ -179,27 +179,27 @@ TEST_P(JsonTest, TestDefaultValues) {
PrintOptions options; PrintOptions options;
options.always_print_primitive_fields = true; options.always_print_primitive_fields = true;
EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"boolValue\":false," EXPECT_THAT(ToJson(m, options), IsOkAndHolds(R"({"boolValue":false,)"
"\"int32Value\":0," R"("int32Value":0,)"
"\"int64Value\":\"0\"," R"("int64Value":"0",)"
"\"uint32Value\":0," R"("uint32Value":0,)"
"\"uint64Value\":\"0\"," R"("uint64Value":"0",)"
"\"floatValue\":0," R"("floatValue":0,)"
"\"doubleValue\":0," R"("doubleValue":0,)"
"\"stringValue\":\"\"," R"("stringValue":"",)"
"\"bytesValue\":\"\"," R"("bytesValue":"",)"
"\"enumValue\":\"FOO\"," R"("enumValue":"FOO",)"
"\"repeatedBoolValue\":[]," R"("repeatedBoolValue":[],)"
"\"repeatedInt32Value\":[]," R"("repeatedInt32Value":[],)"
"\"repeatedInt64Value\":[]," R"("repeatedInt64Value":[],)"
"\"repeatedUint32Value\":[]," R"("repeatedUint32Value":[],)"
"\"repeatedUint64Value\":[]," R"("repeatedUint64Value":[],)"
"\"repeatedFloatValue\":[]," R"("repeatedFloatValue":[],)"
"\"repeatedDoubleValue\":[]," R"("repeatedDoubleValue":[],)"
"\"repeatedStringValue\":[]," R"("repeatedStringValue":[],)"
"\"repeatedBytesValue\":[]," R"("repeatedBytesValue":[],)"
"\"repeatedEnumValue\":[]," R"("repeatedEnumValue":[],)"
"\"repeatedMessageValue\":[]" R"("repeatedMessageValue":[])"
"}")); "}"));
m.set_string_value("i am a test string value"); m.set_string_value("i am a test string value");
@ -207,32 +207,31 @@ TEST_P(JsonTest, TestDefaultValues) {
m.set_optional_bool_value(false); m.set_optional_bool_value(false);
m.set_optional_string_value(""); m.set_optional_string_value("");
m.set_optional_bytes_value(""); m.set_optional_bytes_value("");
EXPECT_THAT( EXPECT_THAT(ToJson(m, options),
ToJson(m, options), IsOkAndHolds(R"({"boolValue":false,)"
IsOkAndHolds("{\"boolValue\":false," R"("int32Value":0,)"
"\"int32Value\":0," R"("int64Value":"0",)"
"\"int64Value\":\"0\"," R"("uint32Value":0,)"
"\"uint32Value\":0," R"("uint64Value":"0",)"
"\"uint64Value\":\"0\"," R"("floatValue":0,)"
"\"floatValue\":0," R"("doubleValue":0,)"
"\"doubleValue\":0," R"("stringValue":"i am a test string value",)"
"\"stringValue\":\"i am a test string value\"," R"("bytesValue":"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=",)"
"\"bytesValue\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\"," R"("enumValue":"FOO",)"
"\"enumValue\":\"FOO\"," R"("repeatedBoolValue":[],)"
"\"repeatedBoolValue\":[]," R"("repeatedInt32Value":[],)"
"\"repeatedInt32Value\":[]," R"("repeatedInt64Value":[],)"
"\"repeatedInt64Value\":[]," R"("repeatedUint32Value":[],)"
"\"repeatedUint32Value\":[]," R"("repeatedUint64Value":[],)"
"\"repeatedUint64Value\":[]," R"("repeatedFloatValue":[],)"
"\"repeatedFloatValue\":[]," R"("repeatedDoubleValue":[],)"
"\"repeatedDoubleValue\":[]," R"("repeatedStringValue":[],)"
"\"repeatedStringValue\":[]," R"("repeatedBytesValue":[],)"
"\"repeatedBytesValue\":[]," R"("repeatedEnumValue":[],)"
"\"repeatedEnumValue\":[]," R"("repeatedMessageValue":[],)"
"\"repeatedMessageValue\":[]," R"("optionalBoolValue":false,)"
"\"optionalBoolValue\":false," R"("optionalStringValue":"",)"
"\"optionalStringValue\":\"\"," R"("optionalBytesValue":"")"
"\"optionalBytesValue\":\"\""
"}")); "}"));
EXPECT_THAT( EXPECT_THAT(
@ -274,6 +273,79 @@ TEST_P(JsonTest, TestDefaultValues) {
R"(,"cordWithZero":"12\u00003","replacementString":"${unknown}"})")); R"(,"cordWithZero":"12\u00003","replacementString":"${unknown}"})"));
} }
TEST_P(JsonTest, TestAlwaysPrintWithoutPresenceFields) {
TestMessage m;
EXPECT_THAT(ToJson(m), IsOkAndHolds("{}"));
PrintOptions options;
options.always_print_without_presence_fields = true;
EXPECT_THAT(ToJson(m, options), IsOkAndHolds(R"({"boolValue":false,)"
R"("int32Value":0,)"
R"("int64Value":"0",)"
R"("uint32Value":0,)"
R"("uint64Value":"0",)"
R"("floatValue":0,)"
R"("doubleValue":0,)"
R"("stringValue":"",)"
R"("bytesValue":"",)"
R"("enumValue":"FOO",)"
R"("repeatedBoolValue":[],)"
R"("repeatedInt32Value":[],)"
R"("repeatedInt64Value":[],)"
R"("repeatedUint32Value":[],)"
R"("repeatedUint64Value":[],)"
R"("repeatedFloatValue":[],)"
R"("repeatedDoubleValue":[],)"
R"("repeatedStringValue":[],)"
R"("repeatedBytesValue":[],)"
R"("repeatedEnumValue":[],)"
R"("repeatedMessageValue":[])"
"}"));
m.set_string_value("i am a test string value");
m.set_bytes_value("i am a test bytes value");
m.set_optional_bool_value(false);
m.set_optional_string_value("");
m.set_optional_bytes_value("");
EXPECT_THAT(ToJson(m, options),
IsOkAndHolds(R"({"boolValue":false,)"
R"("int32Value":0,)"
R"("int64Value":"0",)"
R"("uint32Value":0,)"
R"("uint64Value":"0",)"
R"("floatValue":0,)"
R"("doubleValue":0,)"
R"("stringValue":"i am a test string value",)"
R"("bytesValue":"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=",)"
R"("enumValue":"FOO",)"
R"("repeatedBoolValue":[],)"
R"("repeatedInt32Value":[],)"
R"("repeatedInt64Value":[],)"
R"("repeatedUint32Value":[],)"
R"("repeatedUint64Value":[],)"
R"("repeatedFloatValue":[],)"
R"("repeatedDoubleValue":[],)"
R"("repeatedStringValue":[],)"
R"("repeatedBytesValue":[],)"
R"("repeatedEnumValue":[],)"
R"("repeatedMessageValue":[],)"
R"("optionalBoolValue":false,)"
R"("optionalStringValue":"",)"
R"("optionalBytesValue":"")"
"}"));
EXPECT_THAT(
ToJson(protobuf_unittest::TestAllTypes(), options),
IsOkAndHolds(
R"({"repeatedInt32":[],"repeatedInt64":[],"repeatedUint32":[],"repeatedUint64":[],)"
R"("repeatedSint32":[],"repeatedSint64":[],"repeatedFixed32":[],"repeatedFixed64":[],)"
R"("repeatedSfixed32":[],"repeatedSfixed64":[],"repeatedFloat":[],"repeatedDouble":[],)"
R"("repeatedBool":[],"repeatedString":[],"repeatedBytes":[],"repeatedgroup":[],)"
R"("repeatedNestedMessage":[],"repeatedForeignMessage":[],"repeatedImportMessage":[],)"
R"("repeatedNestedEnum":[],"repeatedForeignEnum":[],"repeatedImportEnum":[],)"
R"("repeatedStringPiece":[],"repeatedCord":[],"repeatedLazyMessage":[]})"));
}
TEST_P(JsonTest, TestPreserveProtoFieldNames) { TEST_P(JsonTest, TestPreserveProtoFieldNames) {
TestMessage m; TestMessage m;
m.mutable_message_value(); m.mutable_message_value();

Loading…
Cancel
Save