Skip to content

Commit 1f3bf1d

Browse files
protobuf-github-botzhangskz
authored andcommitted
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
1 parent c530216 commit 1f3bf1d

File tree

5 files changed

+158
-58
lines changed

5 files changed

+158
-58
lines changed

src/google/protobuf/json/internal/unparser.cc

+6-2
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,9 @@ absl::Status WriteField(JsonWriter& writer, const Msg<Traits>& msg,
433433
} else if (Traits::IsRepeated(field)) {
434434
return WriteRepeated<Traits>(writer, msg, field);
435435
} else if (Traits::GetSize(field, msg) == 0) {
436-
// We can only get here if always_print_primitive_fields is true.
437-
ABSL_DCHECK(writer.options().always_print_primitive_fields);
436+
// We can only get here if one of the always_print options is true.
437+
ABSL_DCHECK(writer.options().always_print_primitive_fields ||
438+
writer.options().always_print_without_presence_fields);
438439

439440
if (Traits::FieldType(field) == FieldDescriptor::TYPE_GROUP) {
440441
// 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,
464465
Traits::FieldType(field) == FieldDescriptor::TYPE_MESSAGE;
465466
has |= !is_singular_message && !Traits::IsOneof(field);
466467
}
468+
if (writer.options().always_print_without_presence_fields) {
469+
has |= Traits::IsRepeated(field) || Traits::IsImplicitPresence(field);
470+
}
467471

468472
if (has) {
469473
fields.push_back(field);

src/google/protobuf/json/internal/writer.h

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

src/google/protobuf/json/json.cc

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ absl::Status BinaryToJsonStream(google::protobuf::util::TypeResolver* resolver,
3434
opts.preserve_proto_field_names = options.preserve_proto_field_names;
3535
opts.always_print_enums_as_ints = options.always_print_enums_as_ints;
3636
opts.always_print_primitive_fields = options.always_print_primitive_fields;
37+
opts.always_print_without_presence_fields =
38+
options.always_print_without_presence_fields;
3739
opts.unquote_int64_if_possible = options.unquote_int64_if_possible;
3840

3941
// TODO: Drop this setting.
@@ -88,6 +90,8 @@ absl::Status MessageToJsonString(const Message& message, std::string* output,
8890
opts.preserve_proto_field_names = options.preserve_proto_field_names;
8991
opts.always_print_enums_as_ints = options.always_print_enums_as_ints;
9092
opts.always_print_primitive_fields = options.always_print_primitive_fields;
93+
opts.always_print_without_presence_fields =
94+
options.always_print_without_presence_fields;
9195
opts.unquote_int64_if_possible = options.unquote_int64_if_possible;
9296

9397
// TODO: Drop this setting.

src/google/protobuf/json/json.h

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

src/google/protobuf/json/json_test.cc

+120-48
Original file line numberDiff line numberDiff line change
@@ -179,61 +179,60 @@ TEST_P(JsonTest, TestDefaultValues) {
179179

180180
PrintOptions options;
181181
options.always_print_primitive_fields = true;
182-
EXPECT_THAT(ToJson(m, options), IsOkAndHolds("{\"boolValue\":false,"
183-
"\"int32Value\":0,"
184-
"\"int64Value\":\"0\","
185-
"\"uint32Value\":0,"
186-
"\"uint64Value\":\"0\","
187-
"\"floatValue\":0,"
188-
"\"doubleValue\":0,"
189-
"\"stringValue\":\"\","
190-
"\"bytesValue\":\"\","
191-
"\"enumValue\":\"FOO\","
192-
"\"repeatedBoolValue\":[],"
193-
"\"repeatedInt32Value\":[],"
194-
"\"repeatedInt64Value\":[],"
195-
"\"repeatedUint32Value\":[],"
196-
"\"repeatedUint64Value\":[],"
197-
"\"repeatedFloatValue\":[],"
198-
"\"repeatedDoubleValue\":[],"
199-
"\"repeatedStringValue\":[],"
200-
"\"repeatedBytesValue\":[],"
201-
"\"repeatedEnumValue\":[],"
202-
"\"repeatedMessageValue\":[]"
182+
EXPECT_THAT(ToJson(m, options), IsOkAndHolds(R"({"boolValue":false,)"
183+
R"("int32Value":0,)"
184+
R"("int64Value":"0",)"
185+
R"("uint32Value":0,)"
186+
R"("uint64Value":"0",)"
187+
R"("floatValue":0,)"
188+
R"("doubleValue":0,)"
189+
R"("stringValue":"",)"
190+
R"("bytesValue":"",)"
191+
R"("enumValue":"FOO",)"
192+
R"("repeatedBoolValue":[],)"
193+
R"("repeatedInt32Value":[],)"
194+
R"("repeatedInt64Value":[],)"
195+
R"("repeatedUint32Value":[],)"
196+
R"("repeatedUint64Value":[],)"
197+
R"("repeatedFloatValue":[],)"
198+
R"("repeatedDoubleValue":[],)"
199+
R"("repeatedStringValue":[],)"
200+
R"("repeatedBytesValue":[],)"
201+
R"("repeatedEnumValue":[],)"
202+
R"("repeatedMessageValue":[])"
203203
"}"));
204204

205205
m.set_string_value("i am a test string value");
206206
m.set_bytes_value("i am a test bytes value");
207207
m.set_optional_bool_value(false);
208208
m.set_optional_string_value("");
209209
m.set_optional_bytes_value("");
210-
EXPECT_THAT(
211-
ToJson(m, options),
212-
IsOkAndHolds("{\"boolValue\":false,"
213-
"\"int32Value\":0,"
214-
"\"int64Value\":\"0\","
215-
"\"uint32Value\":0,"
216-
"\"uint64Value\":\"0\","
217-
"\"floatValue\":0,"
218-
"\"doubleValue\":0,"
219-
"\"stringValue\":\"i am a test string value\","
220-
"\"bytesValue\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\","
221-
"\"enumValue\":\"FOO\","
222-
"\"repeatedBoolValue\":[],"
223-
"\"repeatedInt32Value\":[],"
224-
"\"repeatedInt64Value\":[],"
225-
"\"repeatedUint32Value\":[],"
226-
"\"repeatedUint64Value\":[],"
227-
"\"repeatedFloatValue\":[],"
228-
"\"repeatedDoubleValue\":[],"
229-
"\"repeatedStringValue\":[],"
230-
"\"repeatedBytesValue\":[],"
231-
"\"repeatedEnumValue\":[],"
232-
"\"repeatedMessageValue\":[],"
233-
"\"optionalBoolValue\":false,"
234-
"\"optionalStringValue\":\"\","
235-
"\"optionalBytesValue\":\"\""
236-
"}"));
210+
EXPECT_THAT(ToJson(m, options),
211+
IsOkAndHolds(R"({"boolValue":false,)"
212+
R"("int32Value":0,)"
213+
R"("int64Value":"0",)"
214+
R"("uint32Value":0,)"
215+
R"("uint64Value":"0",)"
216+
R"("floatValue":0,)"
217+
R"("doubleValue":0,)"
218+
R"("stringValue":"i am a test string value",)"
219+
R"("bytesValue":"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=",)"
220+
R"("enumValue":"FOO",)"
221+
R"("repeatedBoolValue":[],)"
222+
R"("repeatedInt32Value":[],)"
223+
R"("repeatedInt64Value":[],)"
224+
R"("repeatedUint32Value":[],)"
225+
R"("repeatedUint64Value":[],)"
226+
R"("repeatedFloatValue":[],)"
227+
R"("repeatedDoubleValue":[],)"
228+
R"("repeatedStringValue":[],)"
229+
R"("repeatedBytesValue":[],)"
230+
R"("repeatedEnumValue":[],)"
231+
R"("repeatedMessageValue":[],)"
232+
R"("optionalBoolValue":false,)"
233+
R"("optionalStringValue":"",)"
234+
R"("optionalBytesValue":"")"
235+
"}"));
237236

238237
EXPECT_THAT(
239238
ToJson(protobuf_unittest::TestAllTypes(), options),
@@ -274,6 +273,79 @@ TEST_P(JsonTest, TestDefaultValues) {
274273
R"(,"cordWithZero":"12\u00003","replacementString":"${unknown}"})"));
275274
}
276275

276+
TEST_P(JsonTest, TestAlwaysPrintWithoutPresenceFields) {
277+
TestMessage m;
278+
EXPECT_THAT(ToJson(m), IsOkAndHolds("{}"));
279+
280+
PrintOptions options;
281+
options.always_print_without_presence_fields = true;
282+
EXPECT_THAT(ToJson(m, options), IsOkAndHolds(R"({"boolValue":false,)"
283+
R"("int32Value":0,)"
284+
R"("int64Value":"0",)"
285+
R"("uint32Value":0,)"
286+
R"("uint64Value":"0",)"
287+
R"("floatValue":0,)"
288+
R"("doubleValue":0,)"
289+
R"("stringValue":"",)"
290+
R"("bytesValue":"",)"
291+
R"("enumValue":"FOO",)"
292+
R"("repeatedBoolValue":[],)"
293+
R"("repeatedInt32Value":[],)"
294+
R"("repeatedInt64Value":[],)"
295+
R"("repeatedUint32Value":[],)"
296+
R"("repeatedUint64Value":[],)"
297+
R"("repeatedFloatValue":[],)"
298+
R"("repeatedDoubleValue":[],)"
299+
R"("repeatedStringValue":[],)"
300+
R"("repeatedBytesValue":[],)"
301+
R"("repeatedEnumValue":[],)"
302+
R"("repeatedMessageValue":[])"
303+
"}"));
304+
305+
m.set_string_value("i am a test string value");
306+
m.set_bytes_value("i am a test bytes value");
307+
m.set_optional_bool_value(false);
308+
m.set_optional_string_value("");
309+
m.set_optional_bytes_value("");
310+
EXPECT_THAT(ToJson(m, options),
311+
IsOkAndHolds(R"({"boolValue":false,)"
312+
R"("int32Value":0,)"
313+
R"("int64Value":"0",)"
314+
R"("uint32Value":0,)"
315+
R"("uint64Value":"0",)"
316+
R"("floatValue":0,)"
317+
R"("doubleValue":0,)"
318+
R"("stringValue":"i am a test string value",)"
319+
R"("bytesValue":"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=",)"
320+
R"("enumValue":"FOO",)"
321+
R"("repeatedBoolValue":[],)"
322+
R"("repeatedInt32Value":[],)"
323+
R"("repeatedInt64Value":[],)"
324+
R"("repeatedUint32Value":[],)"
325+
R"("repeatedUint64Value":[],)"
326+
R"("repeatedFloatValue":[],)"
327+
R"("repeatedDoubleValue":[],)"
328+
R"("repeatedStringValue":[],)"
329+
R"("repeatedBytesValue":[],)"
330+
R"("repeatedEnumValue":[],)"
331+
R"("repeatedMessageValue":[],)"
332+
R"("optionalBoolValue":false,)"
333+
R"("optionalStringValue":"",)"
334+
R"("optionalBytesValue":"")"
335+
"}"));
336+
337+
EXPECT_THAT(
338+
ToJson(protobuf_unittest::TestAllTypes(), options),
339+
IsOkAndHolds(
340+
R"({"repeatedInt32":[],"repeatedInt64":[],"repeatedUint32":[],"repeatedUint64":[],)"
341+
R"("repeatedSint32":[],"repeatedSint64":[],"repeatedFixed32":[],"repeatedFixed64":[],)"
342+
R"("repeatedSfixed32":[],"repeatedSfixed64":[],"repeatedFloat":[],"repeatedDouble":[],)"
343+
R"("repeatedBool":[],"repeatedString":[],"repeatedBytes":[],"repeatedgroup":[],)"
344+
R"("repeatedNestedMessage":[],"repeatedForeignMessage":[],"repeatedImportMessage":[],)"
345+
R"("repeatedNestedEnum":[],"repeatedForeignEnum":[],"repeatedImportEnum":[],)"
346+
R"("repeatedStringPiece":[],"repeatedCord":[],"repeatedLazyMessage":[]})"));
347+
}
348+
277349
TEST_P(JsonTest, TestPreserveProtoFieldNames) {
278350
TestMessage m;
279351
m.mutable_message_value();

0 commit comments

Comments
 (0)