encoding/protojson: add MarshalOptions.UseProtoNames

UseProtoNames=true uses proto field name in JSON field names.

Change-Id: I23249dc1787d9735bef780b1ef8d294a9c55c043
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/193998
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/encoding/protojson/encode.go b/encoding/protojson/encode.go
index d55786e..1662e40 100644
--- a/encoding/protojson/encode.go
+++ b/encoding/protojson/encode.go
@@ -34,23 +34,27 @@
 	// Marshal will return error if there are any missing required fields.
 	AllowPartial bool
 
+	// UseProtoNames uses proto field name instead of lowerCamelCase name in JSON
+	// field names.
+	UseProtoNames bool
+
 	// UseEnumNumbers emits enum values as numbers.
 	UseEnumNumbers bool
 
 	// EmitUnpopulated specifies whether to emit unpopulated fields. It does not
 	// emit unpopulated oneof fields or unpopulated extension fields.
 	// The JSON value emitted for unpopulated fields are as follows:
-	// ╔═══════╤════════════════════════════╗
-	// ║ JSON  │ Protobuf field             ║
-	// ╠═══════╪════════════════════════════╣
-	// ║ false │ proto3 boolean fields      ║
-	// ║ 0     │ proto3 numeric fields      ║
-	// ║ ""    │ proto3 string/bytes fields ║
-	// ║ null  │ proto2 scalar fields       ║
-	// ║ null  │ message fields             ║
-	// ║ []    │ list fields                ║
-	// ║ {}    │ map fields                 ║
-	// ╚═══════╧════════════════════════════╝
+	//  ╔═══════╤════════════════════════════╗
+	//  ║ JSON  │ Protobuf field             ║
+	//  ╠═══════╪════════════════════════════╣
+	//  ║ false │ proto3 boolean fields      ║
+	//  ║ 0     │ proto3 numeric fields      ║
+	//  ║ ""    │ proto3 string/bytes fields ║
+	//  ║ null  │ proto2 scalar fields       ║
+	//  ║ null  │ message fields             ║
+	//  ║ []    │ list fields                ║
+	//  ║ {}    │ map fields                 ║
+	//  ╚═══════╧════════════════════════════╝
 	EmitUnpopulated bool
 
 	// If Indent is a non-empty string, it causes entries for an Array or Object
@@ -128,6 +132,13 @@
 		}
 
 		name := fd.JSONName()
+		if o.UseProtoNames {
+			name = string(fd.Name())
+			// Use type name for group field name.
+			if fd.Kind() == pref.GroupKind {
+				name = string(fd.Message().Name())
+			}
+		}
 		if err := o.encoder.WriteName(name); err != nil {
 			return err
 		}
diff --git a/encoding/protojson/encode_test.go b/encoding/protojson/encode_test.go
index 4ee275a..9a1c86e 100644
--- a/encoding/protojson/encode_test.go
+++ b/encoding/protojson/encode_test.go
@@ -2175,6 +2175,46 @@
     "47": 47
   }
 }`,
+	}, {
+		desc: "UseProtoNames",
+		mo:   protojson.MarshalOptions{UseProtoNames: true},
+		input: &pb2.Nests{
+			OptNested: &pb2.Nested{},
+			Optgroup: &pb2.Nests_OptGroup{
+				OptString: proto.String("inside a group"),
+				OptNested: &pb2.Nested{
+					OptString: proto.String("nested message inside a group"),
+				},
+				Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
+					OptFixed32: proto.Uint32(47),
+				},
+			},
+			Rptgroup: []*pb2.Nests_RptGroup{
+				{
+					RptString: []string{"hello", "world"},
+				},
+			},
+		},
+		want: `{
+  "opt_nested": {},
+  "OptGroup": {
+    "opt_string": "inside a group",
+    "opt_nested": {
+      "opt_string": "nested message inside a group"
+    },
+    "OptNestedGroup": {
+      "opt_fixed32": 47
+    }
+  },
+  "RptGroup": [
+    {
+      "rpt_string": [
+        "hello",
+        "world"
+      ]
+    }
+  ]
+}`,
 	}}
 
 	for _, tt := range tests {