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)) {
return WriteRepeated<Traits>(writer, msg, field);
} else if (Traits::GetSize(field, msg) == 0) {
// We can only get here if always_print_primitive_fields is true.
ABSL_DCHECK(writer.options().always_print_primitive_fields);
// We can only get here if one of the always_print options is true.
ABSL_DCHECK(writer.options().always_print_primitive_fields ||
writer.options().always_print_without_presence_fields);
if (Traits::FieldType(field) == FieldDescriptor::TYPE_GROUP) {
// 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;
has |= !is_singular_message && !Traits::IsOneof(field);
}
if (writer.options().always_print_without_presence_fields) {
has |= Traits::IsRepeated(field) || Traits::IsImplicitPresence(field);
}
if (has) {
fields.push_back(field);

@ -36,11 +36,21 @@ struct WriterOptions {
// Whether to add spaces, line breaks and indentation to make the JSON output
// easy to read.
bool add_whitespace = false;
// Whether to always print primitive fields. By default proto3 primitive
// fields with default values will be omitted in JSON output. For example, an
// int32 field set to 0 will be omitted. Set this flag to true will override
// the default behavior and print primitive fields regardless of their values.
// Whether to always print the following types of fields even if they would
// otherwise be omitted:
// - Implicit presence fields set to their 0 value
// - 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;
// 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
// strings.
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.always_print_enums_as_ints = options.always_print_enums_as_ints;
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;
// 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.always_print_enums_as_ints = options.always_print_enums_as_ints;
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;
// TODO: Drop this setting.

@ -39,11 +39,21 @@ struct PrintOptions {
// Whether to add spaces, line breaks and indentation to make the JSON output
// easy to read.
bool add_whitespace = false;
// Whether to always print primitive fields. By default proto3 primitive
// fields with default values will be omitted in JSON output. For example, an
// int32 field set to 0 will be omitted. Set this flag to true will override
// the default behavior and print primitive fields regardless of their values.
// Whether to always print the following types of fields even if they would
// otherwise be omitted:
// - Implicit presence fields set to their 0 value
// - 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;
// 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
// strings.
bool always_print_enums_as_ints = false;

@ -179,27 +179,27 @@ TEST_P(JsonTest, TestDefaultValues) {
PrintOptions options;
options.always_print_primitive_fields = true;
EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"boolValue\":false,"
"\"int32Value\":0,"
"\"int64Value\":\"0\","
"\"uint32Value\":0,"
"\"uint64Value\":\"0\","
"\"floatValue\":0,"
"\"doubleValue\":0,"
"\"stringValue\":\"\","
"\"bytesValue\":\"\","
"\"enumValue\":\"FOO\","
"\"repeatedBoolValue\":[],"
"\"repeatedInt32Value\":[],"
"\"repeatedInt64Value\":[],"
"\"repeatedUint32Value\":[],"
"\"repeatedUint64Value\":[],"
"\"repeatedFloatValue\":[],"
"\"repeatedDoubleValue\":[],"
"\"repeatedStringValue\":[],"
"\"repeatedBytesValue\":[],"
"\"repeatedEnumValue\":[],"
"\"repeatedMessageValue\":[]"
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");
@ -207,32 +207,31 @@ TEST_P(JsonTest, TestDefaultValues) {
m.set_optional_bool_value(false);
m.set_optional_string_value("");
m.set_optional_bytes_value("");
EXPECT_THAT(
ToJson(m, options),
IsOkAndHolds("{\"boolValue\":false,"
"\"int32Value\":0,"
"\"int64Value\":\"0\","
"\"uint32Value\":0,"
"\"uint64Value\":\"0\","
"\"floatValue\":0,"
"\"doubleValue\":0,"
"\"stringValue\":\"i am a test string value\","
"\"bytesValue\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\","
"\"enumValue\":\"FOO\","
"\"repeatedBoolValue\":[],"
"\"repeatedInt32Value\":[],"
"\"repeatedInt64Value\":[],"
"\"repeatedUint32Value\":[],"
"\"repeatedUint64Value\":[],"
"\"repeatedFloatValue\":[],"
"\"repeatedDoubleValue\":[],"
"\"repeatedStringValue\":[],"
"\"repeatedBytesValue\":[],"
"\"repeatedEnumValue\":[],"
"\"repeatedMessageValue\":[],"
"\"optionalBoolValue\":false,"
"\"optionalStringValue\":\"\","
"\"optionalBytesValue\":\"\""
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(
@ -274,6 +273,79 @@ TEST_P(JsonTest, TestDefaultValues) {
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) {
TestMessage m;
m.mutable_message_value();

Loading…
Cancel
Save