encoding/jsonpb: add support for unmarshaling wrapper and struct types

Also, fixed unmarshaling of map messages where non-fatal errors were not
propagated up.

Change-Id: I06415b4a4ccd12135f0fdfaa38ccda54866139e7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/168997
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/encoding/jsonpb/decode.go b/encoding/jsonpb/decode.go
index 5ea6fb8..8436c38 100644
--- a/encoding/jsonpb/decode.go
+++ b/encoding/jsonpb/decode.go
@@ -131,13 +131,10 @@
 // unmarshalMessage unmarshals a message into the given protoreflect.Message.
 func (d decoder) unmarshalMessage(m pref.Message) error {
 	var nerr errors.NonFatal
-	var reqNums set.Ints
-	var seenNums set.Ints
 
-	msgType := m.Type()
-	knownFields := m.KnownFields()
-	fieldDescs := msgType.Fields()
-	xtTypes := knownFields.ExtensionTypes()
+	if isCustomType(m.Type().FullName()) {
+		return d.unmarshalCustomType(m)
+	}
 
 	jval, err := d.Read()
 	if !nerr.Merge(err) {
@@ -147,6 +144,24 @@
 		return unexpectedJSONError{jval}
 	}
 
+	if err := d.unmarshalFields(m); !nerr.Merge(err) {
+		return err
+	}
+
+	return nerr.E
+}
+
+// unmarshalFields unmarshals the fields into the given protoreflect.Message.
+func (d decoder) unmarshalFields(m pref.Message) error {
+	var nerr errors.NonFatal
+	var reqNums set.Ints
+	var seenNums set.Ints
+
+	msgType := m.Type()
+	knownFields := m.KnownFields()
+	fieldDescs := msgType.Fields()
+	xtTypes := knownFields.ExtensionTypes()
+
 Loop:
 	for {
 		// Read field name.
@@ -205,20 +220,21 @@
 		}
 		seenNums.Set(num)
 
-		// No need to set values for JSON null.
-		if d.Peek() == json.Null {
+		// No need to set values for JSON null unless the field type is
+		// google.protobuf.Value.
+		if d.Peek() == json.Null && !isKnownValue(fd) {
 			d.Read()
 			continue
 		}
 
 		if cardinality := fd.Cardinality(); cardinality == pref.Repeated {
 			// Map or list fields have cardinality of repeated.
-			if err := d.unmarshalRepeated(fd, knownFields); !nerr.Merge(err) {
+			if err := d.unmarshalRepeated(knownFields, fd); !nerr.Merge(err) {
 				return errors.New("%v|%q: %v", fd.FullName(), name, err)
 			}
 		} else {
 			// Required or optional fields.
-			if err := d.unmarshalSingular(fd, knownFields); !nerr.Merge(err) {
+			if err := d.unmarshalSingular(knownFields, fd); !nerr.Merge(err) {
 				return errors.New("%v|%q: %v", fd.FullName(), name, err)
 			}
 			if cardinality == pref.Required {
@@ -257,7 +273,7 @@
 
 // unmarshalSingular unmarshals to the non-repeated field specified by the given
 // FieldDescriptor.
-func (d decoder) unmarshalSingular(fd pref.FieldDescriptor, knownFields pref.KnownFields) error {
+func (d decoder) unmarshalSingular(knownFields pref.KnownFields, fd pref.FieldDescriptor) error {
 	var val pref.Value
 	var err error
 	num := fd.Number()
@@ -493,16 +509,16 @@
 }
 
 // unmarshalRepeated unmarshals into a repeated field.
-func (d decoder) unmarshalRepeated(fd pref.FieldDescriptor, knownFields pref.KnownFields) error {
+func (d decoder) unmarshalRepeated(knownFields pref.KnownFields, fd pref.FieldDescriptor) error {
 	var nerr errors.NonFatal
 	num := fd.Number()
 	val := knownFields.Get(num)
 	if !fd.IsMap() {
-		if err := d.unmarshalList(fd, val.List()); !nerr.Merge(err) {
+		if err := d.unmarshalList(val.List(), fd); !nerr.Merge(err) {
 			return err
 		}
 	} else {
-		if err := d.unmarshalMap(fd, val.Map()); !nerr.Merge(err) {
+		if err := d.unmarshalMap(val.Map(), fd); !nerr.Merge(err) {
 			return err
 		}
 	}
@@ -510,7 +526,7 @@
 }
 
 // unmarshalList unmarshals into given protoreflect.List.
-func (d decoder) unmarshalList(fd pref.FieldDescriptor, list pref.List) error {
+func (d decoder) unmarshalList(list pref.List, fd pref.FieldDescriptor) error {
 	var nerr errors.NonFatal
 	jval, err := d.Read()
 	if !nerr.Merge(err) {
@@ -555,7 +571,7 @@
 }
 
 // unmarshalMap unmarshals into given protoreflect.Map.
-func (d decoder) unmarshalMap(fd pref.FieldDescriptor, mmap pref.Map) error {
+func (d decoder) unmarshalMap(mmap pref.Map, fd pref.FieldDescriptor) error {
 	var nerr errors.NonFatal
 
 	jval, err := d.Read()
@@ -579,11 +595,12 @@
 	switch valDesc.Kind() {
 	case pref.MessageKind, pref.GroupKind:
 		unmarshalMapValue = func() (pref.Value, error) {
+			var nerr errors.NonFatal
 			m := mmap.NewMessage()
-			if err := d.unmarshalMessage(m); err != nil {
+			if err := d.unmarshalMessage(m); !nerr.Merge(err) {
 				return pref.Value{}, err
 			}
-			return pref.ValueOf(m), nil
+			return pref.ValueOf(m), nerr.E
 		}
 	}
 
diff --git a/encoding/jsonpb/decode_test.go b/encoding/jsonpb/decode_test.go
index 2552275..725c5a8 100644
--- a/encoding/jsonpb/decode_test.go
+++ b/encoding/jsonpb/decode_test.go
@@ -16,6 +16,8 @@
 	"github.com/golang/protobuf/v2/proto"
 	preg "github.com/golang/protobuf/v2/reflect/protoregistry"
 	"github.com/golang/protobuf/v2/runtime/protoiface"
+
+	knownpb "github.com/golang/protobuf/v2/types/known"
 )
 
 func init() {
@@ -754,12 +756,28 @@
 			},
 		},
 	}, {
-		desc:         "repeated scalars containing invalid type",
+		desc:         "repeated string contains invalid UTF8",
+		inputMessage: &pb2.Repeats{},
+		inputText:    `{"rptString": ["` + "abc\xff" + `"]}`,
+		wantMessage: &pb2.Repeats{
+			RptString: []string{"abc\xff"},
+		},
+		wantErr: true,
+	}, {
+		desc:         "repeated messages contain invalid UTF8",
+		inputMessage: &pb2.Nests{},
+		inputText:    `{"rptNested": [{"optString": "` + "abc\xff" + `"}]}`,
+		wantMessage: &pb2.Nests{
+			RptNested: []*pb2.Nested{{OptString: scalar.String("abc\xff")}},
+		},
+		wantErr: true,
+	}, {
+		desc:         "repeated scalars contain invalid type",
 		inputMessage: &pb2.Repeats{},
 		inputText:    `{"rptString": ["hello", null, "world"]}`,
 		wantErr:      true,
 	}, {
-		desc:         "repeated messages containing invalid type",
+		desc:         "repeated messages contain invalid type",
 		inputMessage: &pb2.Nests{},
 		inputText:    `{"rptNested": [{}, null]}`,
 		wantErr:      true,
@@ -938,6 +956,36 @@
 }`,
 		wantErr: true,
 	}, {
+		desc:         "map contains contains message value with invalid UTF8",
+		inputMessage: &pb3.Maps{},
+		inputText: `{
+  "strToNested": {
+    "hello": {
+      "sString": "` + "abc\xff" + `"
+	}
+  }
+}`,
+		wantMessage: &pb3.Maps{
+			StrToNested: map[string]*pb3.Nested{
+				"hello": {SString: "abc\xff"},
+			},
+		},
+		wantErr: true,
+	}, {
+		desc:         "map key contains invalid UTF8",
+		inputMessage: &pb3.Maps{},
+		inputText: `{
+  "strToNested": {
+    "` + "abc\xff" + `": {}
+  }
+}`,
+		wantMessage: &pb3.Maps{
+			StrToNested: map[string]*pb3.Nested{
+				"abc\xff": {},
+			},
+		},
+		wantErr: true,
+	}, {
 		desc:         "extensions of non-repeated fields",
 		inputMessage: &pb2.Extensions{},
 		inputText: `{
@@ -947,8 +995,8 @@
   "[pb2.opt_ext_bool]": true,
   "[pb2.opt_ext_nested]": {
     "optString": "nested in an extension",
-    "opt_nested": {
-      "opt_string": "another nested in an extension"
+    "optNested": {
+      "optString": "another nested in an extension"
     }
   },
   "[pb2.opt_ext_string]": "extension field",
@@ -1146,6 +1194,374 @@
 			})
 			return m
 		}(),
+	}, {
+		desc:         "Empty",
+		inputMessage: &knownpb.Empty{},
+		inputText:    `{}`,
+		wantMessage:  &knownpb.Empty{},
+	}, {
+		desc:         "Empty contains unknown",
+		inputMessage: &knownpb.Empty{},
+		inputText:    `{"unknown": null}`,
+		wantErr:      true,
+	}, {
+		desc:         "BoolValue false",
+		inputMessage: &knownpb.BoolValue{},
+		inputText:    `false`,
+		wantMessage:  &knownpb.BoolValue{},
+	}, {
+		desc:         "BoolValue true",
+		inputMessage: &knownpb.BoolValue{},
+		inputText:    `true`,
+		wantMessage:  &knownpb.BoolValue{Value: true},
+	}, {
+		desc:         "BoolValue invalid value",
+		inputMessage: &knownpb.BoolValue{},
+		inputText:    `{}`,
+		wantErr:      true,
+	}, {
+		desc:         "Int32Value",
+		inputMessage: &knownpb.Int32Value{},
+		inputText:    `42`,
+		wantMessage:  &knownpb.Int32Value{Value: 42},
+	}, {
+		desc:         "Int32Value in JSON string",
+		inputMessage: &knownpb.Int32Value{},
+		inputText:    `"1.23e3"`,
+		wantMessage:  &knownpb.Int32Value{Value: 1230},
+	}, {
+		desc:         "Int64Value",
+		inputMessage: &knownpb.Int64Value{},
+		inputText:    `"42"`,
+		wantMessage:  &knownpb.Int64Value{Value: 42},
+	}, {
+		desc:         "UInt32Value",
+		inputMessage: &knownpb.UInt32Value{},
+		inputText:    `42`,
+		wantMessage:  &knownpb.UInt32Value{Value: 42},
+	}, {
+		desc:         "UInt64Value",
+		inputMessage: &knownpb.UInt64Value{},
+		inputText:    `"42"`,
+		wantMessage:  &knownpb.UInt64Value{Value: 42},
+	}, {
+		desc:         "FloatValue",
+		inputMessage: &knownpb.FloatValue{},
+		inputText:    `1.02`,
+		wantMessage:  &knownpb.FloatValue{Value: 1.02},
+	}, {
+		desc:         "FloatValue exceeds max limit",
+		inputMessage: &knownpb.FloatValue{},
+		inputText:    `1.23+40`,
+		wantErr:      true,
+	}, {
+		desc:         "FloatValue Infinity",
+		inputMessage: &knownpb.FloatValue{},
+		inputText:    `"-Infinity"`,
+		wantMessage:  &knownpb.FloatValue{Value: float32(math.Inf(-1))},
+	}, {
+		desc:         "DoubleValue",
+		inputMessage: &knownpb.DoubleValue{},
+		inputText:    `1.02`,
+		wantMessage:  &knownpb.DoubleValue{Value: 1.02},
+	}, {
+		desc:         "DoubleValue Infinity",
+		inputMessage: &knownpb.DoubleValue{},
+		inputText:    `"Infinity"`,
+		wantMessage:  &knownpb.DoubleValue{Value: math.Inf(+1)},
+	}, {
+		desc:         "StringValue empty",
+		inputMessage: &knownpb.StringValue{},
+		inputText:    `""`,
+		wantMessage:  &knownpb.StringValue{},
+	}, {
+		desc:         "StringValue",
+		inputMessage: &knownpb.StringValue{},
+		inputText:    `"谷歌"`,
+		wantMessage:  &knownpb.StringValue{Value: "谷歌"},
+	}, {
+		desc:         "StringValue with invalid UTF8 error",
+		inputMessage: &knownpb.StringValue{},
+		inputText:    "\"abc\xff\"",
+		wantMessage:  &knownpb.StringValue{Value: "abc\xff"},
+		wantErr:      true,
+	}, {
+		desc:         "StringValue field with invalid UTF8 error",
+		inputMessage: &pb2.KnownTypes{},
+		inputText:    "{\n  \"optString\": \"abc\xff\"\n}",
+		wantMessage: &pb2.KnownTypes{
+			OptString: &knownpb.StringValue{Value: "abc\xff"},
+		},
+		wantErr: true,
+	}, {
+		desc:         "BytesValue",
+		inputMessage: &knownpb.BytesValue{},
+		inputText:    `"aGVsbG8="`,
+		wantMessage:  &knownpb.BytesValue{Value: []byte("hello")},
+	}, {
+		desc:         "Value null",
+		inputMessage: &knownpb.Value{},
+		inputText:    `null`,
+		wantMessage:  &knownpb.Value{Kind: &knownpb.Value_NullValue{}},
+	}, {
+		desc:         "Value field null",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optValue": null
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptValue: &knownpb.Value{Kind: &knownpb.Value_NullValue{}},
+		},
+	}, {
+		desc:         "Value bool",
+		inputMessage: &knownpb.Value{},
+		inputText:    `false`,
+		wantMessage:  &knownpb.Value{Kind: &knownpb.Value_BoolValue{}},
+	}, {
+		desc:         "Value field bool",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optValue": true
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptValue: &knownpb.Value{Kind: &knownpb.Value_BoolValue{true}},
+		},
+	}, {
+		desc:         "Value number",
+		inputMessage: &knownpb.Value{},
+		inputText:    `1.02`,
+		wantMessage:  &knownpb.Value{Kind: &knownpb.Value_NumberValue{1.02}},
+	}, {
+		desc:         "Value field number",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optValue": 1.02
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptValue: &knownpb.Value{Kind: &knownpb.Value_NumberValue{1.02}},
+		},
+	}, {
+		desc:         "Value string",
+		inputMessage: &knownpb.Value{},
+		inputText:    `"hello"`,
+		wantMessage:  &knownpb.Value{Kind: &knownpb.Value_StringValue{"hello"}},
+	}, {
+		desc:         "Value string with invalid UTF8",
+		inputMessage: &knownpb.Value{},
+		inputText:    "\"\xff\"",
+		wantMessage:  &knownpb.Value{Kind: &knownpb.Value_StringValue{"\xff"}},
+		wantErr:      true,
+	}, {
+		desc:         "Value field string",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optValue": "NaN"
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptValue: &knownpb.Value{Kind: &knownpb.Value_StringValue{"NaN"}},
+		},
+	}, {
+		desc:         "Value field string with invalid UTF8",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optValue": "` + "\xff" + `"
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptValue: &knownpb.Value{Kind: &knownpb.Value_StringValue{"\xff"}},
+		},
+		wantErr: true,
+	}, {
+		desc:         "Value empty struct",
+		inputMessage: &knownpb.Value{},
+		inputText:    `{}`,
+		wantMessage: &knownpb.Value{
+			Kind: &knownpb.Value_StructValue{
+				&knownpb.Struct{Fields: map[string]*knownpb.Value{}},
+			},
+		},
+	}, {
+		desc:         "Value struct",
+		inputMessage: &knownpb.Value{},
+		inputText: `{
+  "string": "hello",
+  "number": 123,
+  "null": null,
+  "bool": false,
+  "struct": {
+    "string": "world"
+  },
+  "list": []
+}`,
+		wantMessage: &knownpb.Value{
+			Kind: &knownpb.Value_StructValue{
+				&knownpb.Struct{
+					Fields: map[string]*knownpb.Value{
+						"string": {Kind: &knownpb.Value_StringValue{"hello"}},
+						"number": {Kind: &knownpb.Value_NumberValue{123}},
+						"null":   {Kind: &knownpb.Value_NullValue{}},
+						"bool":   {Kind: &knownpb.Value_BoolValue{false}},
+						"struct": {
+							Kind: &knownpb.Value_StructValue{
+								&knownpb.Struct{
+									Fields: map[string]*knownpb.Value{
+										"string": {Kind: &knownpb.Value_StringValue{"world"}},
+									},
+								},
+							},
+						},
+						"list": {
+							Kind: &knownpb.Value_ListValue{&knownpb.ListValue{}},
+						},
+					},
+				},
+			},
+		},
+	}, {
+		desc:         "Value struct with invalid UTF8 string",
+		inputMessage: &knownpb.Value{},
+		inputText:    "{\"string\": \"abc\xff\"}",
+		wantMessage: &knownpb.Value{
+			Kind: &knownpb.Value_StructValue{
+				&knownpb.Struct{
+					Fields: map[string]*knownpb.Value{
+						"string": {Kind: &knownpb.Value_StringValue{"abc\xff"}},
+					},
+				},
+			},
+		},
+		wantErr: true,
+	}, {
+		desc:         "Value field struct",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optValue": {
+    "string": "hello"
+  }
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptValue: &knownpb.Value{
+				Kind: &knownpb.Value_StructValue{
+					&knownpb.Struct{
+						Fields: map[string]*knownpb.Value{
+							"string": {Kind: &knownpb.Value_StringValue{"hello"}},
+						},
+					},
+				},
+			},
+		},
+	}, {
+		desc:         "Value empty list",
+		inputMessage: &knownpb.Value{},
+		inputText:    `[]`,
+		wantMessage: &knownpb.Value{
+			Kind: &knownpb.Value_ListValue{
+				&knownpb.ListValue{Values: []*knownpb.Value{}},
+			},
+		},
+	}, {
+		desc:         "Value list",
+		inputMessage: &knownpb.Value{},
+		inputText: `[
+  "string",
+  123,
+  null,
+  true,
+  {},
+  [
+    "string",
+	1.23,
+	null,
+	false
+  ]
+]`,
+		wantMessage: &knownpb.Value{
+			Kind: &knownpb.Value_ListValue{
+				&knownpb.ListValue{
+					Values: []*knownpb.Value{
+						{Kind: &knownpb.Value_StringValue{"string"}},
+						{Kind: &knownpb.Value_NumberValue{123}},
+						{Kind: &knownpb.Value_NullValue{}},
+						{Kind: &knownpb.Value_BoolValue{true}},
+						{Kind: &knownpb.Value_StructValue{&knownpb.Struct{}}},
+						{
+							Kind: &knownpb.Value_ListValue{
+								&knownpb.ListValue{
+									Values: []*knownpb.Value{
+										{Kind: &knownpb.Value_StringValue{"string"}},
+										{Kind: &knownpb.Value_NumberValue{1.23}},
+										{Kind: &knownpb.Value_NullValue{}},
+										{Kind: &knownpb.Value_BoolValue{false}},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}, {
+		desc:         "Value list with invalid UTF8 string",
+		inputMessage: &knownpb.Value{},
+		inputText:    "[\"abc\xff\"]",
+		wantMessage: &knownpb.Value{
+			Kind: &knownpb.Value_ListValue{
+				&knownpb.ListValue{
+					Values: []*knownpb.Value{
+						{Kind: &knownpb.Value_StringValue{"abc\xff"}},
+					},
+				},
+			},
+		},
+		wantErr: true,
+	}, {
+		desc:         "Value field list with invalid UTF8 string",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optValue": [ "` + "abc\xff" + `"]
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptValue: &knownpb.Value{
+				Kind: &knownpb.Value_ListValue{
+					&knownpb.ListValue{
+						Values: []*knownpb.Value{
+							{Kind: &knownpb.Value_StringValue{"abc\xff"}},
+						},
+					},
+				},
+			},
+		},
+		wantErr: true,
+	}, {
+		desc:         "FieldMask empty",
+		inputMessage: &knownpb.FieldMask{},
+		inputText:    `""`,
+		wantMessage:  &knownpb.FieldMask{Paths: []string{}},
+	}, {
+		desc:         "FieldMask",
+		inputMessage: &knownpb.FieldMask{},
+		inputText:    `"foo,fooBar , foo.barQux ,Foo"`,
+		wantMessage: &knownpb.FieldMask{
+			Paths: []string{
+				"foo",
+				"foo_bar",
+				"foo.bar_qux",
+				"_foo",
+			},
+		},
+	}, {
+		desc:         "FieldMask field",
+		inputMessage: &pb2.KnownTypes{},
+		inputText: `{
+  "optFieldmask": "foo, qux.fooBar"
+}`,
+		wantMessage: &pb2.KnownTypes{
+			OptFieldmask: &knownpb.FieldMask{
+				Paths: []string{
+					"foo",
+					"qux.foo_bar",
+				},
+			},
+		},
 	}}
 
 	for _, tt := range tests {
diff --git a/encoding/jsonpb/encode_test.go b/encoding/jsonpb/encode_test.go
index d6a2e62..09cb9f8 100644
--- a/encoding/jsonpb/encode_test.go
+++ b/encoding/jsonpb/encode_test.go
@@ -976,10 +976,18 @@
 		input: &knownpb.FloatValue{Value: 1.02},
 		want:  `1.02`,
 	}, {
+		desc:  "FloatValue Infinity",
+		input: &knownpb.FloatValue{Value: float32(math.Inf(-1))},
+		want:  `"-Infinity"`,
+	}, {
 		desc:  "DoubleValue",
 		input: &knownpb.DoubleValue{Value: 1.02},
 		want:  `1.02`,
 	}, {
+		desc:  "DoubleValue NaN",
+		input: &knownpb.DoubleValue{Value: math.NaN()},
+		want:  `"NaN"`,
+	}, {
 		desc:  "StringValue empty",
 		input: &knownpb.StringValue{},
 		want:  `""`,
diff --git a/encoding/jsonpb/well_known_types.go b/encoding/jsonpb/well_known_types.go
index 101e4ba..1ff8854 100644
--- a/encoding/jsonpb/well_known_types.go
+++ b/encoding/jsonpb/well_known_types.go
@@ -9,6 +9,7 @@
 	"strings"
 	"time"
 
+	"github.com/golang/protobuf/v2/internal/encoding/json"
 	"github.com/golang/protobuf/v2/internal/errors"
 	"github.com/golang/protobuf/v2/internal/fieldnum"
 	"github.com/golang/protobuf/v2/proto"
@@ -79,7 +80,7 @@
 		return e.marshalFieldMask(m)
 	}
 
-	panic(fmt.Sprintf("encoder.marshalCustomTypes(%q) does not have a custom marshaler", name))
+	panic(fmt.Sprintf("%q does not have a custom marshaler", name))
 }
 
 func (e encoder) marshalAny(m pref.Message) error {
@@ -329,3 +330,184 @@
 func isASCIIUpper(c byte) bool {
 	return 'A' <= c && c <= 'Z'
 }
+
+// unmarshalCustomType unmarshals given well-known type message that have
+// special JSON conversion rules. It needs to be a message type where
+// isCustomType returns true, else it will panic.
+func (d decoder) unmarshalCustomType(m pref.Message) error {
+	name := m.Type().FullName()
+	switch name {
+	case "google.protobuf.Any",
+		"google.protobuf.Duration",
+		"google.protobuf.Timestamp":
+		panic(fmt.Sprintf("unmarshaling of %v is not implemented yet", name))
+
+	case "google.protobuf.BoolValue",
+		"google.protobuf.DoubleValue",
+		"google.protobuf.FloatValue",
+		"google.protobuf.Int32Value",
+		"google.protobuf.Int64Value",
+		"google.protobuf.UInt32Value",
+		"google.protobuf.UInt64Value",
+		"google.protobuf.StringValue",
+		"google.protobuf.BytesValue":
+		return d.unmarshalKnownScalar(m)
+
+	case "google.protobuf.Struct":
+		return d.unmarshalStruct(m)
+
+	case "google.protobuf.ListValue":
+		return d.unmarshalListValue(m)
+
+	case "google.protobuf.Value":
+		return d.unmarshalKnownValue(m)
+
+	case "google.protobuf.FieldMask":
+		return d.unmarshalFieldMask(m)
+	}
+
+	panic(fmt.Sprintf("%q does not have a custom unmarshaler", name))
+}
+
+func (d decoder) unmarshalKnownScalar(m pref.Message) error {
+	var nerr errors.NonFatal
+	msgType := m.Type()
+	fieldDescs := msgType.Fields()
+	knownFields := m.KnownFields()
+
+	// The "value" field has the same field number for all wrapper types.
+	const num = fieldnum.BoolValue_Value
+	fd := fieldDescs.ByNumber(num)
+	val, err := d.unmarshalScalar(fd)
+	if !nerr.Merge(err) {
+		return err
+	}
+	knownFields.Set(num, val)
+	return nerr.E
+}
+
+func (d decoder) unmarshalStruct(m pref.Message) error {
+	msgType := m.Type()
+	fieldDescs := msgType.Fields()
+	knownFields := m.KnownFields()
+
+	fd := fieldDescs.ByNumber(fieldnum.Struct_Fields)
+	val := knownFields.Get(fieldnum.Struct_Fields)
+	return d.unmarshalMap(val.Map(), fd)
+}
+
+func (d decoder) unmarshalListValue(m pref.Message) error {
+	msgType := m.Type()
+	fieldDescs := msgType.Fields()
+	knownFields := m.KnownFields()
+
+	fd := fieldDescs.ByNumber(fieldnum.ListValue_Values)
+	val := knownFields.Get(fieldnum.ListValue_Values)
+	return d.unmarshalList(val.List(), fd)
+}
+
+func isKnownValue(fd pref.FieldDescriptor) bool {
+	md := fd.MessageType()
+	return md != nil && md.FullName() == "google.protobuf.Value"
+}
+
+func (d decoder) unmarshalKnownValue(m pref.Message) error {
+	var nerr errors.NonFatal
+	knownFields := m.KnownFields()
+
+	switch d.Peek() {
+	case json.Null:
+		d.Read()
+		knownFields.Set(fieldnum.Value_NullValue, pref.ValueOf(pref.EnumNumber(0)))
+
+	case json.Bool:
+		jval, err := d.Read()
+		if err != nil {
+			return err
+		}
+		val, err := unmarshalBool(jval)
+		if err != nil {
+			return err
+		}
+		knownFields.Set(fieldnum.Value_BoolValue, val)
+
+	case json.Number:
+		jval, err := d.Read()
+		if err != nil {
+			return err
+		}
+		val, err := unmarshalFloat(jval, 64)
+		if err != nil {
+			return err
+		}
+		knownFields.Set(fieldnum.Value_NumberValue, val)
+
+	case json.String:
+		// A JSON string may have been encoded from the number_value field,
+		// e.g. "NaN", "Infinity", etc. Parsing a proto double type also allows
+		// for it to be in JSON string form. Given this custom encoding spec,
+		// however, there is no way to identify that and hence a JSON string is
+		// always assigned to the string_value field, which means that certain
+		// encoding cannot be parsed back to the same field.
+		jval, err := d.Read()
+		if !nerr.Merge(err) {
+			return err
+		}
+		val, err := unmarshalString(jval)
+		if !nerr.Merge(err) {
+			return err
+		}
+		knownFields.Set(fieldnum.Value_StringValue, val)
+
+	case json.StartObject:
+		m := knownFields.NewMessage(fieldnum.Value_StructValue)
+		if err := d.unmarshalStruct(m); !nerr.Merge(err) {
+			return err
+		}
+		knownFields.Set(fieldnum.Value_StructValue, pref.ValueOf(m))
+
+	case json.StartArray:
+		m := knownFields.NewMessage(fieldnum.Value_ListValue)
+		if err := d.unmarshalListValue(m); !nerr.Merge(err) {
+			return err
+		}
+		knownFields.Set(fieldnum.Value_ListValue, pref.ValueOf(m))
+
+	default:
+		jval, err := d.Read()
+		if err != nil {
+			return err
+		}
+		return unexpectedJSONError{jval}
+	}
+
+	return nerr.E
+}
+
+func (d decoder) unmarshalFieldMask(m pref.Message) error {
+	var nerr errors.NonFatal
+	jval, err := d.Read()
+	if !nerr.Merge(err) {
+		return err
+	}
+	if jval.Type() != json.String {
+		return unexpectedJSONError{jval}
+	}
+	str := strings.TrimSpace(jval.String())
+	if str == "" {
+		return nil
+	}
+	paths := strings.Split(str, ",")
+
+	knownFields := m.KnownFields()
+	val := knownFields.Get(fieldnum.FieldMask_Paths)
+	list := val.List()
+
+	for _, s := range paths {
+		s = strings.TrimSpace(s)
+		// Convert to snake_case. Unlike encoding, no validation is done because
+		// it is not possible to know the original path names.
+		list.Append(pref.ValueOf(snakeCase(s)))
+	}
+	return nil
+}