github.com/jhump/protoreflect@v1.16.0/dynamic/json_test.go (about) 1 package dynamic 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "reflect" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/golang/protobuf/jsonpb" 13 "github.com/golang/protobuf/proto" 14 "github.com/golang/protobuf/ptypes" 15 "google.golang.org/protobuf/types/descriptorpb" 16 "google.golang.org/protobuf/types/known/anypb" 17 "google.golang.org/protobuf/types/known/durationpb" 18 "google.golang.org/protobuf/types/known/structpb" 19 "google.golang.org/protobuf/types/known/timestamppb" 20 "google.golang.org/protobuf/types/known/wrapperspb" 21 22 "github.com/jhump/protoreflect/desc" 23 "github.com/jhump/protoreflect/internal/testprotos" 24 "github.com/jhump/protoreflect/internal/testutil" 25 ) 26 27 func TestJSONUnaryFields(t *testing.T) { 28 jsonTranslationParty(t, unaryFieldsPosMsg, false) 29 jsonTranslationParty(t, unaryFieldsNegMsg, false) 30 jsonTranslationParty(t, unaryFieldsPosInfMsg, false) 31 jsonTranslationParty(t, unaryFieldsNegInfMsg, false) 32 jsonTranslationParty(t, unaryFieldsNanMsg, true) 33 } 34 35 func TestJSONRepeatedFields(t *testing.T) { 36 jsonTranslationParty(t, repeatedFieldsMsg, false) 37 jsonTranslationParty(t, repeatedFieldsInfNanMsg, true) 38 } 39 40 func TestJSONMapKeyFields(t *testing.T) { 41 jsonTranslationParty(t, mapKeyFieldsMsg, false) 42 } 43 44 func TestJSONMapValueFields(t *testing.T) { 45 jsonTranslationParty(t, mapValueFieldsMsg, false) 46 jsonTranslationParty(t, mapValueFieldsInfNanMsg, true) 47 jsonTranslationParty(t, mapValueFieldsNilMsg, false) 48 jsonTranslationParty(t, mapValueFieldsNilUnknownMsg, false) 49 } 50 51 func TestJSONExtensionFields(t *testing.T) { 52 // TODO 53 } 54 55 func createTestFileDescriptor(t *testing.T, packageName string) *desc.FileDescriptor { 56 // Create a new type that could only be resolved via custom resolver 57 // because it does not exist in compiled form 58 fdp := descriptorpb.FileDescriptorProto{ 59 Name: proto.String(fmt.Sprintf("%s.proto", packageName)), 60 Dependency: []string{"google/protobuf/any.proto"}, 61 Package: proto.String(packageName), 62 MessageType: []*descriptorpb.DescriptorProto{ 63 { 64 Name: proto.String("MyMessage"), 65 Field: []*descriptorpb.FieldDescriptorProto{ 66 { 67 Name: proto.String("abc"), 68 Number: proto.Int(1), 69 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 70 Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), 71 }, 72 { 73 Name: proto.String("def"), 74 Number: proto.Int(2), 75 Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(), 76 Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(), 77 }, 78 { 79 Name: proto.String("ghi"), 80 Number: proto.Int(3), 81 Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(), 82 Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(), 83 TypeName: proto.String(".google.protobuf.Any"), 84 }, 85 }, 86 }, 87 }, 88 } 89 anyfd, err := desc.LoadFileDescriptor("google/protobuf/any.proto") 90 testutil.Ok(t, err) 91 fd, err := desc.CreateFileDescriptor(&fdp, anyfd) 92 testutil.Ok(t, err) 93 return fd 94 } 95 96 func TestJSONAnyResolver(t *testing.T) { 97 fd1 := createTestFileDescriptor(t, "foobar") 98 fd2 := createTestFileDescriptor(t, "snafu") 99 md := fd1.FindMessage("foobar.MyMessage") 100 dm := NewMessage(md) 101 dm.SetFieldByNumber(1, "fubar") 102 dm.SetFieldByNumber(2, int32(123)) 103 a1, err := ptypes.MarshalAny(dm) 104 testutil.Ok(t, err) 105 md = fd2.FindMessage("snafu.MyMessage") 106 dm = NewMessage(md) 107 dm.SetFieldByNumber(1, "snafu") 108 dm.SetFieldByNumber(2, int32(456)) 109 a2, err := ptypes.MarshalAny(dm) 110 testutil.Ok(t, err) 111 112 msg := &testprotos.TestWellKnownTypes{Extras: []*anypb.Any{a1, a2}} 113 resolver := AnyResolver(nil, fd1, fd2) 114 115 jsm := jsonpb.Marshaler{AnyResolver: resolver} 116 js, err := jsm.MarshalToString(msg) 117 testutil.Ok(t, err) 118 expected := `{"extras":[{"@type":"type.googleapis.com/foobar.MyMessage","abc":"fubar","def":123},{"@type":"type.googleapis.com/snafu.MyMessage","abc":"snafu","def":456}]}` 119 testutil.Eq(t, expected, js) 120 121 jsu := jsonpb.Unmarshaler{AnyResolver: resolver} 122 msg2 := &testprotos.TestWellKnownTypes{} 123 err = jsu.Unmarshal(strings.NewReader(js), msg2) 124 testutil.Ok(t, err) 125 126 testutil.Ceq(t, msg, msg2, eqpm) 127 } 128 129 func TestJSONAnyResolver_AutomaticForDynamicMessage(t *testing.T) { 130 // marshaling and unmarshaling a dynamic message automatically enables 131 // resolving Any messages for known types (a known type is one that is 132 // in the dynamic message's file descriptor or that file's transitive 133 // dependencies) 134 fd := createTestFileDescriptor(t, "foobar") 135 md := fd.FindMessage("foobar.MyMessage") 136 dm := NewMessageFactoryWithDefaults().NewMessage(md).(*Message) 137 dm.SetFieldByNumber(1, "fubar") 138 dm.SetFieldByNumber(2, int32(123)) 139 a1, err := ptypes.MarshalAny(dm) 140 testutil.Ok(t, err) 141 dm.SetFieldByNumber(1, "snafu") 142 dm.SetFieldByNumber(2, int32(456)) 143 a2, err := ptypes.MarshalAny(dm) 144 testutil.Ok(t, err) 145 dm.SetFieldByNumber(1, "xyz") 146 dm.SetFieldByNumber(2, int32(-987)) 147 dm.SetFieldByNumber(3, []*anypb.Any{a1, a2}) 148 149 js, err := dm.MarshalJSON() 150 testutil.Ok(t, err) 151 expected := `{"abc":"xyz","def":-987,"ghi":[{"@type":"type.googleapis.com/foobar.MyMessage","abc":"fubar","def":123},{"@type":"type.googleapis.com/foobar.MyMessage","abc":"snafu","def":456}]}` 152 testutil.Eq(t, expected, string(js)) 153 154 dm2 := NewMessageFactoryWithDefaults().NewMessage(md).(*Message) 155 err = dm2.UnmarshalJSON(js) 156 testutil.Ok(t, err) 157 158 testutil.Ceq(t, dm, dm2, eqdm) 159 } 160 161 func TestMarshalJSONEmitDefaults(t *testing.T) { 162 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.ReallySimpleMessage)(nil)) 163 testutil.Ok(t, err) 164 dm := NewMessage(md) 165 js, err := dm.MarshalJSON() 166 testutil.Ok(t, err) 167 testutil.Eq(t, `{}`, string(js)) 168 jsDefaults, err := dm.MarshalJSONPB(&jsonpb.Marshaler{EmitDefaults: true}) 169 testutil.Ok(t, err) 170 testutil.Eq(t, `{"id":"0","name":""}`, string(jsDefaults)) 171 } 172 173 func TestMarshalJSONEmitDefaultsMapKeyFields(t *testing.T) { 174 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.MapKeyFields)(nil)) 175 testutil.Ok(t, err) 176 dm := NewMessage(md) 177 m := &jsonpb.Marshaler{EmitDefaults: true} 178 jsDefaults, err := dm.MarshalJSONPB(m) 179 testutil.Ok(t, err) 180 testutil.Eq(t, `{"i":{},"j":{},"k":{},"l":{},"m":{},"n":{},"o":{},"p":{},"q":{},"r":{},"s":{},"t":{}}`, string(jsDefaults)) 181 182 jsDefaults2, err := m.MarshalToString(&testprotos.MapKeyFields{}) 183 testutil.Ok(t, err) 184 testutil.Eq(t, string(jsDefaults), string(jsDefaults2)) 185 } 186 187 func TestMarshalJSONEmitDefaultsOneOfFields(t *testing.T) { 188 // we don't include default values for fields in a one-of 189 // since it would not round-trip correctly 190 testCases := []struct { 191 msg *testprotos.OneOfMessage 192 expectedJson string 193 }{ 194 { 195 msg: &testprotos.OneOfMessage{}, 196 expectedJson: `{}`, 197 }, 198 { 199 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_IntValue{IntValue: 12345}}, 200 expectedJson: `{"intValue":12345}`, 201 }, 202 { 203 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_StringValue{StringValue: "foobar"}}, 204 expectedJson: `{"stringValue":"foobar"}`, 205 }, 206 { 207 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_MsgValue{MsgValue: &testprotos.OneOfMessage{}}}, 208 expectedJson: `{"msgValue":{}}`, 209 }, 210 { 211 msg: &testprotos.OneOfMessage{Value: &testprotos.OneOfMessage_MsgValue{MsgValue: nil}}, 212 expectedJson: `{"msgValue":null}`, 213 }, 214 } 215 m := &jsonpb.Marshaler{EmitDefaults: true} 216 for _, testCase := range testCases { 217 dm, err := AsDynamicMessageWithMessageFactory(testCase.msg, NewMessageFactoryWithDefaults()) 218 testutil.Ok(t, err) 219 asJson, err := dm.MarshalJSONPB(m) 220 testutil.Ok(t, err) 221 actualJson := string(asJson) 222 testutil.Eq(t, testCase.expectedJson, actualJson) 223 224 // round-trip 225 err = jsonpb.UnmarshalString(actualJson, dm) 226 testutil.Ok(t, err) 227 var roundtripped testprotos.OneOfMessage 228 err = dm.ConvertTo(&roundtripped) 229 testutil.Ok(t, err) 230 testutil.Ceq(t, testCase.msg, &roundtripped, eqpm) 231 } 232 } 233 234 func TestMarshalJSONEnumsAsInts(t *testing.T) { 235 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestRequest)(nil)) 236 testutil.Ok(t, err) 237 dm := NewMessage(md) 238 dm.SetFieldByNumber(1, []int32{1}) 239 dm.SetFieldByNumber(2, "bedazzle") 240 js, err := dm.MarshalJSONPB(&jsonpb.Marshaler{EnumsAsInts: true}) 241 testutil.Ok(t, err) 242 testutil.Eq(t, `{"foo":[1],"bar":"bedazzle"}`, string(js)) 243 } 244 245 func TestMarshalJSONOrigName(t *testing.T) { 246 // TODO 247 } 248 249 func TestMarshalJSONIndent(t *testing.T) { 250 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestRequest)(nil)) 251 testutil.Ok(t, err) 252 dm := NewMessage(md) 253 dm.SetFieldByNumber(1, []int32{1}) 254 dm.SetFieldByNumber(2, "bedazzle") 255 js, err := dm.MarshalJSON() 256 testutil.Ok(t, err) 257 testutil.Eq(t, `{"foo":["VALUE1"],"bar":"bedazzle"}`, string(js)) 258 jsIndent, err := dm.MarshalJSONIndent() 259 testutil.Ok(t, err) 260 testutil.Eq(t, `{ 261 "foo": [ 262 "VALUE1" 263 ], 264 "bar": "bedazzle" 265 }`, string(jsIndent)) 266 jsIndent, err = dm.MarshalJSONPB(&jsonpb.Marshaler{Indent: "\t"}) 267 testutil.Ok(t, err) 268 testutil.Eq(t, `{ 269 "foo": [ 270 "VALUE1" 271 ], 272 "bar": "bedazzle" 273 }`, string(jsIndent)) 274 } 275 276 func TestMarshalJSONIndentEmbedWellKnownTypes(t *testing.T) { 277 // testing the formatting of dynamic message that embeds non-dynamic message, 278 // both those w/ special/simple JSON encoding (like timestamp) and those with 279 // more structure (Any). 280 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestWellKnownTypes)(nil)) 281 testutil.Ok(t, err) 282 dm := NewMessage(md) 283 284 ts, err := ptypes.TimestampProto(time.Date(2010, 3, 4, 5, 6, 7, 809000, time.UTC)) 285 testutil.Ok(t, err) 286 dm.SetFieldByNumber(1, ts) 287 288 anys := make([]*anypb.Any, 3) 289 anys[0], err = ptypes.MarshalAny(&testprotos.TestRequest{Bar: "foo"}) 290 testutil.Ok(t, err) 291 anys[1], err = ptypes.MarshalAny(&testprotos.TestRequest{Bar: "bar"}) 292 testutil.Ok(t, err) 293 anys[2], err = ptypes.MarshalAny(&testprotos.TestRequest{Bar: "baz"}) 294 testutil.Ok(t, err) 295 dm.SetFieldByNumber(13, anys) 296 297 js, err := dm.MarshalJSON() 298 testutil.Ok(t, err) 299 testutil.Eq(t, `{"startTime":"2010-03-04T05:06:07.000809Z","extras":[{"@type":"type.googleapis.com/testprotos.TestRequest","bar":"foo"},{"@type":"type.googleapis.com/testprotos.TestRequest","bar":"bar"},{"@type":"type.googleapis.com/testprotos.TestRequest","bar":"baz"}]}`, string(js)) 300 jsIndent, err := dm.MarshalJSONIndent() 301 testutil.Ok(t, err) 302 testutil.Eq(t, `{ 303 "startTime": "2010-03-04T05:06:07.000809Z", 304 "extras": [ 305 { 306 "@type": "type.googleapis.com/testprotos.TestRequest", 307 "bar": "foo" 308 }, 309 { 310 "@type": "type.googleapis.com/testprotos.TestRequest", 311 "bar": "bar" 312 }, 313 { 314 "@type": "type.googleapis.com/testprotos.TestRequest", 315 "bar": "baz" 316 } 317 ] 318 }`, string(jsIndent)) 319 jsIndent, err = dm.MarshalJSONPB(&jsonpb.Marshaler{Indent: "\t"}) 320 testutil.Ok(t, err) 321 testutil.Eq(t, `{ 322 "startTime": "2010-03-04T05:06:07.000809Z", 323 "extras": [ 324 { 325 "@type": "type.googleapis.com/testprotos.TestRequest", 326 "bar": "foo" 327 }, 328 { 329 "@type": "type.googleapis.com/testprotos.TestRequest", 330 "bar": "bar" 331 }, 332 { 333 "@type": "type.googleapis.com/testprotos.TestRequest", 334 "bar": "baz" 335 } 336 ] 337 }`, string(jsIndent)) 338 } 339 340 func TestUnmarshalJSONAllowUnknownFields(t *testing.T) { 341 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestRequest)(nil)) 342 testutil.Ok(t, err) 343 js := []byte(`{"foo":["VALUE1"],"bar":"bedazzle","xxx": 1}`) 344 dm := NewMessage(md) 345 err = dm.UnmarshalJSON(js) 346 testutil.Nok(t, err) 347 unmarshaler := &jsonpb.Unmarshaler{AllowUnknownFields: true} 348 err = dm.UnmarshalJSONPB(unmarshaler, js) 349 testutil.Ok(t, err) 350 foo := dm.GetFieldByNumber(1) 351 bar := dm.GetFieldByNumber(2) 352 testutil.Eq(t, []int32{1}, foo) 353 testutil.Eq(t, "bedazzle", bar) 354 } 355 356 func TestUnmarshalJSONValueFromList(t *testing.T) { 357 js := `{"list":[1,2,3]}` 358 md, err := desc.LoadMessageDescriptorForMessage((*testprotos.SimpleValue)(nil)) 359 testutil.Ok(t, err) 360 dm := NewMessage(md) 361 testutil.Ok(t, err) 362 unmarshaler := &jsonpb.Unmarshaler{} 363 err = dm.UnmarshalJSONPB(unmarshaler, []byte(js)) 364 testutil.Ok(t, err) 365 msg := reflect.New(reflect.TypeOf(&testprotos.SimpleValue{}).Elem()).Interface().(proto.Message) 366 err = unmarshaler.Unmarshal(strings.NewReader(js), msg) 367 testutil.Ok(t, err) 368 dm2 := NewMessage(md) 369 err = dm2.ConvertFrom(msg) 370 testutil.Ok(t, err) 371 testutil.Ceq(t, dm2, dm, eqpm) 372 } 373 374 func TestJSONWellKnownType(t *testing.T) { 375 any1, err := ptypes.MarshalAny(&testprotos.TestRequest{ 376 Foo: []testprotos.Proto3Enum{testprotos.Proto3Enum_VALUE1, testprotos.Proto3Enum_VALUE2}, 377 Bar: "bar", 378 Baz: &testprotos.TestMessage{Ne: []testprotos.TestMessage_NestedEnum{testprotos.TestMessage_VALUE1}}, 379 }) 380 testutil.Ok(t, err) 381 any2, err := ptypes.MarshalAny(ptypes.TimestampNow()) 382 testutil.Ok(t, err) 383 384 wkts := &testprotos.TestWellKnownTypes{ 385 StartTime: ×tamppb.Timestamp{Seconds: 1010101, Nanos: 20202}, 386 Elapsed: &durationpb.Duration{Seconds: 30303, Nanos: 40404}, 387 Dbl: &wrapperspb.DoubleValue{Value: 3.14159}, 388 Flt: &wrapperspb.FloatValue{Value: -1.0101010}, 389 Bl: &wrapperspb.BoolValue{Value: true}, 390 I32: &wrapperspb.Int32Value{Value: -42}, 391 I64: &wrapperspb.Int64Value{Value: -9090909090}, 392 U32: &wrapperspb.UInt32Value{Value: 42}, 393 U64: &wrapperspb.UInt64Value{Value: 9090909090}, 394 Str: &wrapperspb.StringValue{Value: "foobar"}, 395 Byt: &wrapperspb.BytesValue{Value: []byte("snafu")}, 396 Json: []*structpb.Value{ 397 {Kind: &structpb.Value_BoolValue{BoolValue: true}}, 398 {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: []*structpb.Value{ 399 {Kind: &structpb.Value_NullValue{}}, 400 {Kind: &structpb.Value_StringValue{StringValue: "fubar"}}, 401 {Kind: &structpb.Value_NumberValue{NumberValue: 10101.20202}}, 402 }}}}, 403 {Kind: &structpb.Value_StructValue{StructValue: &structpb.Struct{Fields: map[string]*structpb.Value{ 404 "foo": {Kind: &structpb.Value_NullValue{}}, 405 "bar": {Kind: &structpb.Value_StringValue{StringValue: "snafu"}}, 406 "baz": {Kind: &structpb.Value_NumberValue{NumberValue: 30303.40404}}, 407 }}}}, 408 }, 409 Extras: []*anypb.Any{any1, any2}, 410 } 411 412 jsm := jsonpb.Marshaler{} 413 js, err := jsm.MarshalToString(wkts) 414 testutil.Ok(t, err) 415 416 md, err := desc.LoadMessageDescriptorForMessage(wkts) 417 testutil.Ok(t, err) 418 dm := NewMessage(md) 419 err = dm.UnmarshalJSON([]byte(js)) 420 testutil.Ok(t, err) 421 422 // check that the unmarshalled fields were constructed correctly with the 423 // right value and type (e.g. generated well-known-type, not dynamic message) 424 ts, ok := dm.GetFieldByNumber(1).(*timestamppb.Timestamp) 425 testutil.Require(t, ok) 426 testutil.Ceq(t, wkts.StartTime, ts, eqpm) 427 428 dur, ok := dm.GetFieldByNumber(2).(*durationpb.Duration) 429 testutil.Require(t, ok) 430 testutil.Ceq(t, wkts.Elapsed, dur, eqpm) 431 432 dbl, ok := dm.GetFieldByNumber(3).(*wrapperspb.DoubleValue) 433 testutil.Require(t, ok) 434 testutil.Eq(t, wkts.Dbl.Value, dbl.Value) 435 436 flt, ok := dm.GetFieldByNumber(4).(*wrapperspb.FloatValue) 437 testutil.Require(t, ok) 438 testutil.Eq(t, wkts.Flt.Value, flt.Value) 439 440 bl, ok := dm.GetFieldByNumber(5).(*wrapperspb.BoolValue) 441 testutil.Require(t, ok) 442 testutil.Eq(t, wkts.Bl.Value, bl.Value) 443 444 i32, ok := dm.GetFieldByNumber(6).(*wrapperspb.Int32Value) 445 testutil.Require(t, ok) 446 testutil.Eq(t, wkts.I32.Value, i32.Value) 447 448 i64, ok := dm.GetFieldByNumber(7).(*wrapperspb.Int64Value) 449 testutil.Require(t, ok) 450 testutil.Eq(t, wkts.I64.Value, i64.Value) 451 452 u32, ok := dm.GetFieldByNumber(8).(*wrapperspb.UInt32Value) 453 testutil.Require(t, ok) 454 testutil.Eq(t, wkts.U32.Value, u32.Value) 455 456 u64, ok := dm.GetFieldByNumber(9).(*wrapperspb.UInt64Value) 457 testutil.Require(t, ok) 458 testutil.Eq(t, wkts.U64.Value, u64.Value) 459 460 str, ok := dm.GetFieldByNumber(10).(*wrapperspb.StringValue) 461 testutil.Require(t, ok) 462 testutil.Eq(t, wkts.Str.Value, str.Value) 463 464 byt, ok := dm.GetFieldByNumber(11).(*wrapperspb.BytesValue) 465 testutil.Require(t, ok) 466 testutil.Eq(t, wkts.Byt.Value, byt.Value) 467 468 vals, ok := dm.GetFieldByNumber(12).([]interface{}) 469 testutil.Require(t, ok) 470 testutil.Eq(t, len(wkts.Json), len(vals)) 471 for i := range vals { 472 v, ok := vals[i].(*structpb.Value) 473 testutil.Require(t, ok) 474 testutil.Ceq(t, wkts.Json[i], v, eqpm) 475 } 476 477 extras, ok := dm.GetFieldByNumber(13).([]interface{}) 478 testutil.Require(t, ok) 479 testutil.Eq(t, len(wkts.Extras), len(extras)) 480 for i := range extras { 481 v, ok := extras[i].(*anypb.Any) 482 testutil.Require(t, ok) 483 testutil.Eq(t, wkts.Extras[i].TypeUrl, v.TypeUrl) 484 testutil.Eq(t, wkts.Extras[i].Value, v.Value) 485 } 486 } 487 488 func TestJSONWellKnownTypeFromFileDescriptorSet(t *testing.T) { 489 // TODO: generalize this so it tests all well-known types, not just duration 490 491 data, err := ioutil.ReadFile("../internal/testprotos/duration.protoset") 492 testutil.Ok(t, err) 493 fds := &descriptorpb.FileDescriptorSet{} 494 err = proto.Unmarshal(data, fds) 495 testutil.Ok(t, err) 496 fd, err := desc.CreateFileDescriptorFromSet(fds) 497 testutil.Ok(t, err) 498 md := fd.FindMessage("google.protobuf.Duration") 499 testutil.Neq(t, nil, md) 500 501 dur := &durationpb.Duration{Seconds: 30303, Nanos: 40404} 502 503 // marshal duration to JSON 504 jsm := jsonpb.Marshaler{} 505 js, err := jsm.MarshalToString(dur) 506 testutil.Ok(t, err) 507 508 // make sure we can unmarshal it 509 dm := NewMessage(md) 510 err = dm.UnmarshalJSON([]byte(js)) 511 testutil.Ok(t, err) 512 513 // and then marshal it again with same output as original 514 dynJs, err := jsm.MarshalToString(dm) 515 testutil.Ok(t, err) 516 testutil.Eq(t, js, dynJs) 517 } 518 519 func jsonTranslationParty(t *testing.T, msg proto.Message, includesNaN bool) { 520 doTranslationParty(t, msg, 521 func(pm proto.Message) ([]byte, error) { 522 m := jsonpb.Marshaler{} 523 var b bytes.Buffer 524 err := m.Marshal(&b, pm) 525 if err != nil { 526 return nil, err 527 } else { 528 return b.Bytes(), nil 529 } 530 }, 531 func(b []byte, pm proto.Message) error { 532 return jsonpb.Unmarshal(bytes.NewReader(b), pm) 533 }, 534 (*Message).MarshalJSON, (*Message).UnmarshalJSON, includesNaN, true, true) 535 }