encoding: Add EmitDefaultValues option

Introduce the EmitDefaultValues in addition to the existing
EmitUnpopulated option.

EmitDefaultValues is added to emit json messages more compatible with
the `always_print_primitive_fields` option of the cpp protobuf library.

EmitUnpopulated overrides EmitDefaultValues since the former generates
a strict superset of the latter.

See descussion:
https://github.com/golang/protobuf/issues/1536

Change-Id: Ib29b69d630fa3e8d8fdeb0de43b5683f30152151
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/521215
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Cassondra Foesch <cfoesch@gmail.com>
Reviewed-by: Lasse Folger <lassefolger@google.com>
diff --git a/encoding/protojson/encode.go b/encoding/protojson/encode.go
index 97f1f7f..3f75098 100644
--- a/encoding/protojson/encode.go
+++ b/encoding/protojson/encode.go
@@ -81,6 +81,25 @@
 	//  ╚═══════╧════════════════════════════╝
 	EmitUnpopulated bool
 
+	// EmitDefaultValues specifies whether to emit default-valued primitive fields,
+	// empty lists, and empty maps. The fields affected are as follows:
+	//  ╔═══════╤════════════════════════════════════════╗
+	//  ║ JSON  │ Protobuf field                         ║
+	//  ╠═══════╪════════════════════════════════════════╣
+	//  ║ false │ non-optional scalar boolean fields     ║
+	//  ║ 0     │ non-optional scalar numeric fields     ║
+	//  ║ ""    │ non-optional scalar string/byte fields ║
+	//  ║ []    │ empty repeated fields                  ║
+	//  ║ {}    │ empty map fields                       ║
+	//  ╚═══════╧════════════════════════════════════════╝
+	//
+	// Behaves similarly to EmitUnpopulated, but does not emit "null"-value fields,
+	// i.e. presence-sensing fields that are omitted will remain omitted to preserve
+	// presence-sensing.
+	// EmitUnpopulated takes precedence over EmitDefaultValues since the former generates
+	// a strict superset of the latter.
+	EmitDefaultValues bool
+
 	// Resolver is used for looking up types when expanding google.protobuf.Any
 	// messages. If nil, this defaults to using protoregistry.GlobalTypes.
 	Resolver interface {
@@ -178,7 +197,11 @@
 
 // unpopulatedFieldRanger wraps a protoreflect.Message and modifies its Range
 // method to additionally iterate over unpopulated fields.
-type unpopulatedFieldRanger struct{ protoreflect.Message }
+type unpopulatedFieldRanger struct {
+	protoreflect.Message
+
+	skipNull bool
+}
 
 func (m unpopulatedFieldRanger) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
 	fds := m.Descriptor().Fields()
@@ -192,6 +215,9 @@
 		isProto2Scalar := fd.Syntax() == protoreflect.Proto2 && fd.Default().IsValid()
 		isSingularMessage := fd.Cardinality() != protoreflect.Repeated && fd.Message() != nil
 		if isProto2Scalar || isSingularMessage {
+			if m.skipNull {
+				continue
+			}
 			v = protoreflect.Value{} // use invalid value to emit null
 		}
 		if !f(fd, v) {
@@ -217,8 +243,11 @@
 	defer e.EndObject()
 
 	var fields order.FieldRanger = m
-	if e.opts.EmitUnpopulated {
-		fields = unpopulatedFieldRanger{m}
+	switch {
+	case e.opts.EmitUnpopulated:
+		fields = unpopulatedFieldRanger{Message: m, skipNull: false}
+	case e.opts.EmitDefaultValues:
+		fields = unpopulatedFieldRanger{Message: m, skipNull: true}
 	}
 	if typeURL != "" {
 		fields = typeURLFieldRanger{fields, typeURL}
diff --git a/encoding/protojson/encode_test.go b/encoding/protojson/encode_test.go
index adda076..63ddb78 100644
--- a/encoding/protojson/encode_test.go
+++ b/encoding/protojson/encode_test.go
@@ -2194,6 +2194,222 @@
   "optString": null
 }`,
 	}, {
+		desc: "EmitUnpopulated overrides EmitDefaultValues",
+		mo:   protojson.MarshalOptions{EmitUnpopulated: true, EmitDefaultValues: true},
+		input: &pb2.Nests{
+			RptNested: []*pb2.Nested{nil, {}},
+		},
+		want: `{
+  "optNested": null,
+  "optgroup": null,
+  "rptNested": [
+    {
+      "optString": null,
+      "optNested": null
+    },
+    {
+      "optString": null,
+      "optNested": null
+    }
+  ],
+  "rptgroup": []
+}`,
+	}, {
+		desc:  "EmitDefaultValues: proto2 optional scalars",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb2.Scalars{},
+		want:  `{}`,
+	}, {
+		desc:  "EmitDefaultValues: proto3 scalars",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb3.Scalars{},
+		want: `{
+  "sBool": false,
+  "sInt32": 0,
+  "sInt64": "0",
+  "sUint32": 0,
+  "sUint64": "0",
+  "sSint32": 0,
+  "sSint64": "0",
+  "sFixed32": 0,
+  "sFixed64": "0",
+  "sSfixed32": 0,
+  "sSfixed64": "0",
+  "sFloat": 0,
+  "sDouble": 0,
+  "sBytes": "",
+  "sString": ""
+}`,
+	}, {
+		desc:  "EmitDefaultValues: proto2 enum",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb2.Enums{},
+		want: `{
+  "rptEnum": [],
+  "rptNestedEnum": []
+}`,
+	}, {
+		desc:  "EmitDefaultValues: proto3 enum",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb3.Enums{},
+		want: `{
+  "sEnum": "ZERO",
+  "sNestedEnum": "CERO"
+}`,
+	}, {
+		desc:  "EmitDefaultValues: proto2 message and group fields",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb2.Nests{},
+		want: `{
+  "rptNested": [],
+  "rptgroup": []
+}`,
+	}, {
+		desc:  "EmitDefaultValues: proto3 message field",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb3.Nests{},
+		want:  `{}`,
+	}, {
+		desc: "EmitDefaultValues: proto2 empty message and group fields",
+		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb2.Nests{
+			OptNested: &pb2.Nested{},
+			Optgroup:  &pb2.Nests_OptGroup{},
+		},
+		want: `{
+  "optNested": {},
+  "optgroup": {},
+  "rptNested": [],
+  "rptgroup": []
+}`,
+	}, {
+		desc: "EmitDefaultValues: proto3 empty message field",
+		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb3.Nests{
+			SNested: &pb3.Nested{},
+		},
+		want: `{
+  "sNested": {
+    "sString": ""
+  }
+}`,
+	}, {
+		desc: "EmitDefaultValues: proto2 required fields",
+		mo: protojson.MarshalOptions{
+			AllowPartial:      true,
+			EmitDefaultValues: true,
+		},
+		input: &pb2.Requireds{},
+		want:  `{}`,
+	}, {
+		desc:  "EmitDefaultValues: repeated fields",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb2.Repeats{},
+		want: `{
+  "rptBool": [],
+  "rptInt32": [],
+  "rptInt64": [],
+  "rptUint32": [],
+  "rptUint64": [],
+  "rptFloat": [],
+  "rptDouble": [],
+  "rptString": [],
+  "rptBytes": []
+}`,
+	}, {
+		desc: "EmitDefaultValues: repeated containing empty message",
+		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb2.Nests{
+			RptNested: []*pb2.Nested{nil, {}},
+		},
+		want: `{
+  "rptNested": [
+    {},
+    {}
+  ],
+  "rptgroup": []
+}`,
+	}, {
+		desc:  "EmitDefaultValues: map fields",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb3.Maps{},
+		want: `{
+  "int32ToStr": {},
+  "boolToUint32": {},
+  "uint64ToEnum": {},
+  "strToNested": {},
+  "strToOneofs": {}
+}`,
+	}, {
+		desc: "EmitDefaultValues: map containing empty message",
+		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb3.Maps{
+			StrToNested: map[string]*pb3.Nested{
+				"nested": &pb3.Nested{},
+			},
+			StrToOneofs: map[string]*pb3.Oneofs{
+				"nested": &pb3.Oneofs{},
+			},
+		},
+		want: `{
+  "int32ToStr": {},
+  "boolToUint32": {},
+  "uint64ToEnum": {},
+  "strToNested": {
+    "nested": {
+      "sString": ""
+    }
+  },
+  "strToOneofs": {
+    "nested": {}
+  }
+}`,
+	}, {
+		desc:  "EmitDefaultValues: oneof fields",
+		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb3.Oneofs{},
+		want:  `{}`,
+	}, {
+		desc: "EmitDefaultValues: extensions",
+		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
+		input: func() proto.Message {
+			m := &pb2.Extensions{}
+			proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{})
+			proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
+				nil,
+				{},
+			})
+			return m
+		}(),
+		want: `{
+  "[pb2.opt_ext_nested]": {},
+  "[pb2.rpt_ext_nested]": [
+    {},
+    {}
+  ]
+}`,
+	}, {
+		desc: "EmitDefaultValues: with populated fields",
+		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
+		input: &pb2.Scalars{
+			OptInt32:    proto.Int32(0xff),
+			OptUint32:   proto.Uint32(47),
+			OptSint32:   proto.Int32(-1001),
+			OptFixed32:  proto.Uint32(32),
+			OptSfixed32: proto.Int32(-32),
+			OptFloat:    proto.Float32(1.02),
+			OptBytes:    []byte("谷歌"),
+		},
+		want: `{
+  "optInt32": 255,
+  "optUint32": 47,
+  "optSint32": -1001,
+  "optFixed32": 32,
+  "optSfixed32": -32,
+  "optFloat": 1.02,
+  "optBytes": "6LC35q2M"
+}`,
+	}, {
 		desc: "UseEnumNumbers in singular field",
 		mo:   protojson.MarshalOptions{UseEnumNumbers: true},
 		input: &pb2.Enums{