1
2
3
4
5 package protojson_test
6
7 import (
8 "bytes"
9 "math"
10 "testing"
11
12 "github.com/google/go-cmp/cmp"
13
14 "google.golang.org/protobuf/encoding/protojson"
15 "google.golang.org/protobuf/internal/detrand"
16 "google.golang.org/protobuf/internal/flags"
17 "google.golang.org/protobuf/proto"
18 "google.golang.org/protobuf/reflect/protoregistry"
19 "google.golang.org/protobuf/testing/protopack"
20
21 pb2 "google.golang.org/protobuf/internal/testprotos/textpb2"
22 pb3 "google.golang.org/protobuf/internal/testprotos/textpb3"
23 "google.golang.org/protobuf/types/known/anypb"
24 "google.golang.org/protobuf/types/known/durationpb"
25 "google.golang.org/protobuf/types/known/emptypb"
26 "google.golang.org/protobuf/types/known/fieldmaskpb"
27 "google.golang.org/protobuf/types/known/structpb"
28 "google.golang.org/protobuf/types/known/timestamppb"
29 "google.golang.org/protobuf/types/known/wrapperspb"
30 )
31
32
33 func init() { detrand.Disable() }
34
35 func TestMarshal(t *testing.T) {
36 tests := []struct {
37 desc string
38 mo protojson.MarshalOptions
39 input proto.Message
40 want string
41 wantErr bool
42 skip bool
43 }{{
44 desc: "proto2 optional scalars not set",
45 input: &pb2.Scalars{},
46 want: "{}",
47 }, {
48 desc: "proto3 scalars not set",
49 input: &pb3.Scalars{},
50 want: "{}",
51 }, {
52 desc: "proto3 optional not set",
53 input: &pb3.Proto3Optional{},
54 want: "{}",
55 }, {
56 desc: "proto2 optional scalars set to zero values",
57 input: &pb2.Scalars{
58 OptBool: proto.Bool(false),
59 OptInt32: proto.Int32(0),
60 OptInt64: proto.Int64(0),
61 OptUint32: proto.Uint32(0),
62 OptUint64: proto.Uint64(0),
63 OptSint32: proto.Int32(0),
64 OptSint64: proto.Int64(0),
65 OptFixed32: proto.Uint32(0),
66 OptFixed64: proto.Uint64(0),
67 OptSfixed32: proto.Int32(0),
68 OptSfixed64: proto.Int64(0),
69 OptFloat: proto.Float32(0),
70 OptDouble: proto.Float64(0),
71 OptBytes: []byte{},
72 OptString: proto.String(""),
73 },
74 want: `{
75 "optBool": false,
76 "optInt32": 0,
77 "optInt64": "0",
78 "optUint32": 0,
79 "optUint64": "0",
80 "optSint32": 0,
81 "optSint64": "0",
82 "optFixed32": 0,
83 "optFixed64": "0",
84 "optSfixed32": 0,
85 "optSfixed64": "0",
86 "optFloat": 0,
87 "optDouble": 0,
88 "optBytes": "",
89 "optString": ""
90 }`,
91 }, {
92 desc: "proto3 optional set to zero values",
93 input: &pb3.Proto3Optional{
94 OptBool: proto.Bool(false),
95 OptInt32: proto.Int32(0),
96 OptInt64: proto.Int64(0),
97 OptUint32: proto.Uint32(0),
98 OptUint64: proto.Uint64(0),
99 OptFloat: proto.Float32(0),
100 OptDouble: proto.Float64(0),
101 OptString: proto.String(""),
102 OptBytes: []byte{},
103 OptEnum: pb3.Enum_ZERO.Enum(),
104 OptMessage: &pb3.Nested{},
105 },
106 want: `{
107 "optBool": false,
108 "optInt32": 0,
109 "optInt64": "0",
110 "optUint32": 0,
111 "optUint64": "0",
112 "optFloat": 0,
113 "optDouble": 0,
114 "optString": "",
115 "optBytes": "",
116 "optEnum": "ZERO",
117 "optMessage": {}
118 }`,
119 }, {
120 desc: "proto2 optional scalars set to some values",
121 input: &pb2.Scalars{
122 OptBool: proto.Bool(true),
123 OptInt32: proto.Int32(0xff),
124 OptInt64: proto.Int64(0xdeadbeef),
125 OptUint32: proto.Uint32(47),
126 OptUint64: proto.Uint64(0xdeadbeef),
127 OptSint32: proto.Int32(-1001),
128 OptSint64: proto.Int64(-0xffff),
129 OptFixed64: proto.Uint64(64),
130 OptSfixed32: proto.Int32(-32),
131 OptFloat: proto.Float32(1.02),
132 OptDouble: proto.Float64(1.234),
133 OptBytes: []byte("谷歌"),
134 OptString: proto.String("谷歌"),
135 },
136 want: `{
137 "optBool": true,
138 "optInt32": 255,
139 "optInt64": "3735928559",
140 "optUint32": 47,
141 "optUint64": "3735928559",
142 "optSint32": -1001,
143 "optSint64": "-65535",
144 "optFixed64": "64",
145 "optSfixed32": -32,
146 "optFloat": 1.02,
147 "optDouble": 1.234,
148 "optBytes": "6LC35q2M",
149 "optString": "谷歌"
150 }`,
151 }, {
152 desc: "string",
153 input: &pb3.Scalars{
154 SString: "谷歌",
155 },
156 want: `{
157 "sString": "谷歌"
158 }`,
159 }, {
160 desc: "string with invalid UTF8",
161 input: &pb3.Scalars{
162 SString: "abc\xff",
163 },
164 wantErr: true,
165 }, {
166 desc: "float nan",
167 input: &pb3.Scalars{
168 SFloat: float32(math.NaN()),
169 },
170 want: `{
171 "sFloat": "NaN"
172 }`,
173 }, {
174 desc: "float positive infinity",
175 input: &pb3.Scalars{
176 SFloat: float32(math.Inf(1)),
177 },
178 want: `{
179 "sFloat": "Infinity"
180 }`,
181 }, {
182 desc: "float negative infinity",
183 input: &pb3.Scalars{
184 SFloat: float32(math.Inf(-1)),
185 },
186 want: `{
187 "sFloat": "-Infinity"
188 }`,
189 }, {
190 desc: "double nan",
191 input: &pb3.Scalars{
192 SDouble: math.NaN(),
193 },
194 want: `{
195 "sDouble": "NaN"
196 }`,
197 }, {
198 desc: "double positive infinity",
199 input: &pb3.Scalars{
200 SDouble: math.Inf(1),
201 },
202 want: `{
203 "sDouble": "Infinity"
204 }`,
205 }, {
206 desc: "double negative infinity",
207 input: &pb3.Scalars{
208 SDouble: math.Inf(-1),
209 },
210 want: `{
211 "sDouble": "-Infinity"
212 }`,
213 }, {
214 desc: "proto2 enum not set",
215 input: &pb2.Enums{},
216 want: "{}",
217 }, {
218 desc: "proto2 enum set to zero value",
219 input: &pb2.Enums{
220 OptEnum: pb2.Enum(0).Enum(),
221 OptNestedEnum: pb2.Enums_NestedEnum(0).Enum(),
222 },
223 want: `{
224 "optEnum": 0,
225 "optNestedEnum": 0
226 }`,
227 }, {
228 desc: "proto2 enum",
229 input: &pb2.Enums{
230 OptEnum: pb2.Enum_ONE.Enum(),
231 OptNestedEnum: pb2.Enums_UNO.Enum(),
232 },
233 want: `{
234 "optEnum": "ONE",
235 "optNestedEnum": "UNO"
236 }`,
237 }, {
238 desc: "proto2 enum set to numeric values",
239 input: &pb2.Enums{
240 OptEnum: pb2.Enum(2).Enum(),
241 OptNestedEnum: pb2.Enums_NestedEnum(2).Enum(),
242 },
243 want: `{
244 "optEnum": "TWO",
245 "optNestedEnum": "DOS"
246 }`,
247 }, {
248 desc: "proto2 enum set to unnamed numeric values",
249 input: &pb2.Enums{
250 OptEnum: pb2.Enum(101).Enum(),
251 OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
252 },
253 want: `{
254 "optEnum": 101,
255 "optNestedEnum": -101
256 }`,
257 }, {
258 desc: "proto3 enum not set",
259 input: &pb3.Enums{},
260 want: "{}",
261 }, {
262 desc: "proto3 enum set to zero value",
263 input: &pb3.Enums{
264 SEnum: pb3.Enum_ZERO,
265 SNestedEnum: pb3.Enums_CERO,
266 },
267 want: "{}",
268 }, {
269 desc: "proto3 enum",
270 input: &pb3.Enums{
271 SEnum: pb3.Enum_ONE,
272 SNestedEnum: pb3.Enums_UNO,
273 },
274 want: `{
275 "sEnum": "ONE",
276 "sNestedEnum": "UNO"
277 }`,
278 }, {
279 desc: "proto3 enum set to numeric values",
280 input: &pb3.Enums{
281 SEnum: 2,
282 SNestedEnum: 2,
283 },
284 want: `{
285 "sEnum": "TWO",
286 "sNestedEnum": "DOS"
287 }`,
288 }, {
289 desc: "proto3 enum set to unnamed numeric values",
290 input: &pb3.Enums{
291 SEnum: -47,
292 SNestedEnum: 47,
293 },
294 want: `{
295 "sEnum": -47,
296 "sNestedEnum": 47
297 }`,
298 }, {
299 desc: "proto2 nested message not set",
300 input: &pb2.Nests{},
301 want: "{}",
302 }, {
303 desc: "proto2 nested message set to empty",
304 input: &pb2.Nests{
305 OptNested: &pb2.Nested{},
306 Optgroup: &pb2.Nests_OptGroup{},
307 },
308 want: `{
309 "optNested": {},
310 "optgroup": {}
311 }`,
312 }, {
313 desc: "proto2 nested messages",
314 input: &pb2.Nests{
315 OptNested: &pb2.Nested{
316 OptString: proto.String("nested message"),
317 OptNested: &pb2.Nested{
318 OptString: proto.String("another nested message"),
319 },
320 },
321 },
322 want: `{
323 "optNested": {
324 "optString": "nested message",
325 "optNested": {
326 "optString": "another nested message"
327 }
328 }
329 }`,
330 }, {
331 desc: "proto2 groups",
332 input: &pb2.Nests{
333 Optgroup: &pb2.Nests_OptGroup{
334 OptString: proto.String("inside a group"),
335 OptNested: &pb2.Nested{
336 OptString: proto.String("nested message inside a group"),
337 },
338 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
339 OptFixed32: proto.Uint32(47),
340 },
341 },
342 },
343 want: `{
344 "optgroup": {
345 "optString": "inside a group",
346 "optNested": {
347 "optString": "nested message inside a group"
348 },
349 "optnestedgroup": {
350 "optFixed32": 47
351 }
352 }
353 }`,
354 }, {
355 desc: "proto3 nested message not set",
356 input: &pb3.Nests{},
357 want: "{}",
358 }, {
359 desc: "proto3 nested message set to empty",
360 input: &pb3.Nests{
361 SNested: &pb3.Nested{},
362 },
363 want: `{
364 "sNested": {}
365 }`,
366 }, {
367 desc: "proto3 nested message",
368 input: &pb3.Nests{
369 SNested: &pb3.Nested{
370 SString: "nested message",
371 SNested: &pb3.Nested{
372 SString: "another nested message",
373 },
374 },
375 },
376 want: `{
377 "sNested": {
378 "sString": "nested message",
379 "sNested": {
380 "sString": "another nested message"
381 }
382 }
383 }`,
384 }, {
385 desc: "oneof not set",
386 input: &pb3.Oneofs{},
387 want: "{}",
388 }, {
389 desc: "oneof set to empty string",
390 input: &pb3.Oneofs{
391 Union: &pb3.Oneofs_OneofString{},
392 },
393 want: `{
394 "oneofString": ""
395 }`,
396 }, {
397 desc: "oneof set to string",
398 input: &pb3.Oneofs{
399 Union: &pb3.Oneofs_OneofString{
400 OneofString: "hello",
401 },
402 },
403 want: `{
404 "oneofString": "hello"
405 }`,
406 }, {
407 desc: "oneof set to enum",
408 input: &pb3.Oneofs{
409 Union: &pb3.Oneofs_OneofEnum{
410 OneofEnum: pb3.Enum_ZERO,
411 },
412 },
413 want: `{
414 "oneofEnum": "ZERO"
415 }`,
416 }, {
417 desc: "oneof set to empty message",
418 input: &pb3.Oneofs{
419 Union: &pb3.Oneofs_OneofNested{
420 OneofNested: &pb3.Nested{},
421 },
422 },
423 want: `{
424 "oneofNested": {}
425 }`,
426 }, {
427 desc: "oneof set to message",
428 input: &pb3.Oneofs{
429 Union: &pb3.Oneofs_OneofNested{
430 OneofNested: &pb3.Nested{
431 SString: "nested message",
432 },
433 },
434 },
435 want: `{
436 "oneofNested": {
437 "sString": "nested message"
438 }
439 }`,
440 }, {
441 desc: "repeated fields not set",
442 input: &pb2.Repeats{},
443 want: "{}",
444 }, {
445 desc: "repeated fields set to empty slices",
446 input: &pb2.Repeats{
447 RptBool: []bool{},
448 RptInt32: []int32{},
449 RptInt64: []int64{},
450 RptUint32: []uint32{},
451 RptUint64: []uint64{},
452 RptFloat: []float32{},
453 RptDouble: []float64{},
454 RptBytes: [][]byte{},
455 },
456 want: "{}",
457 }, {
458 desc: "repeated fields set to some values",
459 input: &pb2.Repeats{
460 RptBool: []bool{true, false, true, true},
461 RptInt32: []int32{1, 6, 0, 0},
462 RptInt64: []int64{-64, 47},
463 RptUint32: []uint32{0xff, 0xffff},
464 RptUint64: []uint64{0xdeadbeef},
465 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
466 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
467 RptString: []string{"hello", "世界"},
468 RptBytes: [][]byte{
469 []byte("hello"),
470 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
471 },
472 },
473 want: `{
474 "rptBool": [
475 true,
476 false,
477 true,
478 true
479 ],
480 "rptInt32": [
481 1,
482 6,
483 0,
484 0
485 ],
486 "rptInt64": [
487 "-64",
488 "47"
489 ],
490 "rptUint32": [
491 255,
492 65535
493 ],
494 "rptUint64": [
495 "3735928559"
496 ],
497 "rptFloat": [
498 "NaN",
499 "Infinity",
500 "-Infinity",
501 1.034
502 ],
503 "rptDouble": [
504 "NaN",
505 "Infinity",
506 "-Infinity",
507 1.23e-308
508 ],
509 "rptString": [
510 "hello",
511 "世界"
512 ],
513 "rptBytes": [
514 "aGVsbG8=",
515 "5LiW55WM"
516 ]
517 }`,
518 }, {
519 desc: "repeated enums",
520 input: &pb2.Enums{
521 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
522 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
523 },
524 want: `{
525 "rptEnum": [
526 "ONE",
527 "TWO",
528 "TEN",
529 42
530 ],
531 "rptNestedEnum": [
532 "DOS",
533 47,
534 "DIEZ"
535 ]
536 }`,
537 }, {
538 desc: "repeated messages set to empty",
539 input: &pb2.Nests{
540 RptNested: []*pb2.Nested{},
541 Rptgroup: []*pb2.Nests_RptGroup{},
542 },
543 want: "{}",
544 }, {
545 desc: "repeated messages",
546 input: &pb2.Nests{
547 RptNested: []*pb2.Nested{
548 {
549 OptString: proto.String("repeat nested one"),
550 },
551 {
552 OptString: proto.String("repeat nested two"),
553 OptNested: &pb2.Nested{
554 OptString: proto.String("inside repeat nested two"),
555 },
556 },
557 {},
558 },
559 },
560 want: `{
561 "rptNested": [
562 {
563 "optString": "repeat nested one"
564 },
565 {
566 "optString": "repeat nested two",
567 "optNested": {
568 "optString": "inside repeat nested two"
569 }
570 },
571 {}
572 ]
573 }`,
574 }, {
575 desc: "repeated messages contains nil value",
576 input: &pb2.Nests{
577 RptNested: []*pb2.Nested{nil, {}},
578 },
579 want: `{
580 "rptNested": [
581 {},
582 {}
583 ]
584 }`,
585 }, {
586 desc: "repeated groups",
587 input: &pb2.Nests{
588 Rptgroup: []*pb2.Nests_RptGroup{
589 {
590 RptString: []string{"hello", "world"},
591 },
592 {},
593 nil,
594 },
595 },
596 want: `{
597 "rptgroup": [
598 {
599 "rptString": [
600 "hello",
601 "world"
602 ]
603 },
604 {},
605 {}
606 ]
607 }`,
608 }, {
609 desc: "map fields not set",
610 input: &pb3.Maps{},
611 want: "{}",
612 }, {
613 desc: "map fields set to empty",
614 input: &pb3.Maps{
615 Int32ToStr: map[int32]string{},
616 BoolToUint32: map[bool]uint32{},
617 Uint64ToEnum: map[uint64]pb3.Enum{},
618 StrToNested: map[string]*pb3.Nested{},
619 StrToOneofs: map[string]*pb3.Oneofs{},
620 },
621 want: "{}",
622 }, {
623 desc: "map fields 1",
624 input: &pb3.Maps{
625 BoolToUint32: map[bool]uint32{
626 true: 42,
627 false: 101,
628 },
629 },
630 want: `{
631 "boolToUint32": {
632 "false": 101,
633 "true": 42
634 }
635 }`,
636 }, {
637 desc: "map fields 2",
638 input: &pb3.Maps{
639 Int32ToStr: map[int32]string{
640 -101: "-101",
641 0xff: "0xff",
642 0: "zero",
643 },
644 },
645 want: `{
646 "int32ToStr": {
647 "-101": "-101",
648 "0": "zero",
649 "255": "0xff"
650 }
651 }`,
652 }, {
653 desc: "map fields 3",
654 input: &pb3.Maps{
655 Uint64ToEnum: map[uint64]pb3.Enum{
656 1: pb3.Enum_ONE,
657 2: pb3.Enum_TWO,
658 10: pb3.Enum_TEN,
659 47: 47,
660 },
661 },
662 want: `{
663 "uint64ToEnum": {
664 "1": "ONE",
665 "2": "TWO",
666 "10": "TEN",
667 "47": 47
668 }
669 }`,
670 }, {
671 desc: "map fields 4",
672 input: &pb3.Maps{
673 StrToNested: map[string]*pb3.Nested{
674 "nested": &pb3.Nested{
675 SString: "nested in a map",
676 },
677 },
678 },
679 want: `{
680 "strToNested": {
681 "nested": {
682 "sString": "nested in a map"
683 }
684 }
685 }`,
686 }, {
687 desc: "map fields 5",
688 input: &pb3.Maps{
689 StrToOneofs: map[string]*pb3.Oneofs{
690 "string": &pb3.Oneofs{
691 Union: &pb3.Oneofs_OneofString{
692 OneofString: "hello",
693 },
694 },
695 "nested": &pb3.Oneofs{
696 Union: &pb3.Oneofs_OneofNested{
697 OneofNested: &pb3.Nested{
698 SString: "nested oneof in map field value",
699 },
700 },
701 },
702 },
703 },
704 want: `{
705 "strToOneofs": {
706 "nested": {
707 "oneofNested": {
708 "sString": "nested oneof in map field value"
709 }
710 },
711 "string": {
712 "oneofString": "hello"
713 }
714 }
715 }`,
716 }, {
717 desc: "map field contains nil value",
718 input: &pb3.Maps{
719 StrToNested: map[string]*pb3.Nested{
720 "nil": nil,
721 },
722 },
723 want: `{
724 "strToNested": {
725 "nil": {}
726 }
727 }`,
728 }, {
729 desc: "required fields not set",
730 input: &pb2.Requireds{},
731 want: `{}`,
732 wantErr: true,
733 }, {
734 desc: "required fields partially set",
735 input: &pb2.Requireds{
736 ReqBool: proto.Bool(false),
737 ReqSfixed64: proto.Int64(0),
738 ReqDouble: proto.Float64(1.23),
739 ReqString: proto.String("hello"),
740 ReqEnum: pb2.Enum_ONE.Enum(),
741 },
742 want: `{
743 "reqBool": false,
744 "reqSfixed64": "0",
745 "reqDouble": 1.23,
746 "reqString": "hello",
747 "reqEnum": "ONE"
748 }`,
749 wantErr: true,
750 }, {
751 desc: "required fields not set with AllowPartial",
752 mo: protojson.MarshalOptions{AllowPartial: true},
753 input: &pb2.Requireds{
754 ReqBool: proto.Bool(false),
755 ReqSfixed64: proto.Int64(0),
756 ReqDouble: proto.Float64(1.23),
757 ReqString: proto.String("hello"),
758 ReqEnum: pb2.Enum_ONE.Enum(),
759 },
760 want: `{
761 "reqBool": false,
762 "reqSfixed64": "0",
763 "reqDouble": 1.23,
764 "reqString": "hello",
765 "reqEnum": "ONE"
766 }`,
767 }, {
768 desc: "required fields all set",
769 input: &pb2.Requireds{
770 ReqBool: proto.Bool(false),
771 ReqSfixed64: proto.Int64(0),
772 ReqDouble: proto.Float64(1.23),
773 ReqString: proto.String("hello"),
774 ReqEnum: pb2.Enum_ONE.Enum(),
775 ReqNested: &pb2.Nested{},
776 },
777 want: `{
778 "reqBool": false,
779 "reqSfixed64": "0",
780 "reqDouble": 1.23,
781 "reqString": "hello",
782 "reqEnum": "ONE",
783 "reqNested": {}
784 }`,
785 }, {
786 desc: "indirect required field",
787 input: &pb2.IndirectRequired{
788 OptNested: &pb2.NestedWithRequired{},
789 },
790 want: `{
791 "optNested": {}
792 }`,
793 wantErr: true,
794 }, {
795 desc: "indirect required field with AllowPartial",
796 mo: protojson.MarshalOptions{AllowPartial: true},
797 input: &pb2.IndirectRequired{
798 OptNested: &pb2.NestedWithRequired{},
799 },
800 want: `{
801 "optNested": {}
802 }`,
803 }, {
804 desc: "indirect required field in empty repeated",
805 input: &pb2.IndirectRequired{
806 RptNested: []*pb2.NestedWithRequired{},
807 },
808 want: `{}`,
809 }, {
810 desc: "indirect required field in repeated",
811 input: &pb2.IndirectRequired{
812 RptNested: []*pb2.NestedWithRequired{
813 &pb2.NestedWithRequired{},
814 },
815 },
816 want: `{
817 "rptNested": [
818 {}
819 ]
820 }`,
821 wantErr: true,
822 }, {
823 desc: "indirect required field in repeated with AllowPartial",
824 mo: protojson.MarshalOptions{AllowPartial: true},
825 input: &pb2.IndirectRequired{
826 RptNested: []*pb2.NestedWithRequired{
827 &pb2.NestedWithRequired{},
828 },
829 },
830 want: `{
831 "rptNested": [
832 {}
833 ]
834 }`,
835 }, {
836 desc: "indirect required field in empty map",
837 input: &pb2.IndirectRequired{
838 StrToNested: map[string]*pb2.NestedWithRequired{},
839 },
840 want: "{}",
841 }, {
842 desc: "indirect required field in map",
843 input: &pb2.IndirectRequired{
844 StrToNested: map[string]*pb2.NestedWithRequired{
845 "fail": &pb2.NestedWithRequired{},
846 },
847 },
848 want: `{
849 "strToNested": {
850 "fail": {}
851 }
852 }`,
853 wantErr: true,
854 }, {
855 desc: "indirect required field in map with AllowPartial",
856 mo: protojson.MarshalOptions{AllowPartial: true},
857 input: &pb2.IndirectRequired{
858 StrToNested: map[string]*pb2.NestedWithRequired{
859 "fail": &pb2.NestedWithRequired{},
860 },
861 },
862 want: `{
863 "strToNested": {
864 "fail": {}
865 }
866 }`,
867 }, {
868 desc: "indirect required field in oneof",
869 input: &pb2.IndirectRequired{
870 Union: &pb2.IndirectRequired_OneofNested{
871 OneofNested: &pb2.NestedWithRequired{},
872 },
873 },
874 want: `{
875 "oneofNested": {}
876 }`,
877 wantErr: true,
878 }, {
879 desc: "indirect required field in oneof with AllowPartial",
880 mo: protojson.MarshalOptions{AllowPartial: true},
881 input: &pb2.IndirectRequired{
882 Union: &pb2.IndirectRequired_OneofNested{
883 OneofNested: &pb2.NestedWithRequired{},
884 },
885 },
886 want: `{
887 "oneofNested": {}
888 }`,
889 }, {
890 desc: "unknown fields are ignored",
891 input: func() proto.Message {
892 m := &pb2.Scalars{
893 OptString: proto.String("no unknowns"),
894 }
895 m.ProtoReflect().SetUnknown(protopack.Message{
896 protopack.Tag{101, protopack.BytesType}, protopack.String("hello world"),
897 }.Marshal())
898 return m
899 }(),
900 want: `{
901 "optString": "no unknowns"
902 }`,
903 }, {
904 desc: "json_name",
905 input: &pb3.JSONNames{
906 SString: "json_name",
907 },
908 want: `{
909 "foo_bar": "json_name"
910 }`,
911 }, {
912 desc: "extensions of non-repeated fields",
913 input: func() proto.Message {
914 m := &pb2.Extensions{
915 OptString: proto.String("non-extension field"),
916 OptBool: proto.Bool(true),
917 OptInt32: proto.Int32(42),
918 }
919 proto.SetExtension(m, pb2.E_OptExtBool, true)
920 proto.SetExtension(m, pb2.E_OptExtString, "extension field")
921 proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
922 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{
923 OptString: proto.String("nested in an extension"),
924 OptNested: &pb2.Nested{
925 OptString: proto.String("another nested in an extension"),
926 },
927 })
928 return m
929 }(),
930 want: `{
931 "optString": "non-extension field",
932 "optBool": true,
933 "optInt32": 42,
934 "[pb2.opt_ext_bool]": true,
935 "[pb2.opt_ext_enum]": "TEN",
936 "[pb2.opt_ext_nested]": {
937 "optString": "nested in an extension",
938 "optNested": {
939 "optString": "another nested in an extension"
940 }
941 },
942 "[pb2.opt_ext_string]": "extension field"
943 }`,
944 }, {
945 desc: "extensions of repeated fields",
946 input: func() proto.Message {
947 m := &pb2.Extensions{}
948 proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
949 proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47})
950 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
951 &pb2.Nested{OptString: proto.String("one")},
952 &pb2.Nested{OptString: proto.String("two")},
953 &pb2.Nested{OptString: proto.String("three")},
954 })
955 return m
956 }(),
957 want: `{
958 "[pb2.rpt_ext_enum]": [
959 "TEN",
960 101,
961 "ONE"
962 ],
963 "[pb2.rpt_ext_fixed32]": [
964 42,
965 47
966 ],
967 "[pb2.rpt_ext_nested]": [
968 {
969 "optString": "one"
970 },
971 {
972 "optString": "two"
973 },
974 {
975 "optString": "three"
976 }
977 ]
978 }`,
979 }, {
980 desc: "extensions of non-repeated fields in another message",
981 input: func() proto.Message {
982 m := &pb2.Extensions{}
983 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
984 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
985 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
986 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
987 OptString: proto.String("nested in an extension"),
988 OptNested: &pb2.Nested{
989 OptString: proto.String("another nested in an extension"),
990 },
991 })
992 return m
993 }(),
994 want: `{
995 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
996 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
997 "[pb2.ExtensionsContainer.opt_ext_nested]": {
998 "optString": "nested in an extension",
999 "optNested": {
1000 "optString": "another nested in an extension"
1001 }
1002 },
1003 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
1004 }`,
1005 }, {
1006 desc: "extensions of repeated fields in another message",
1007 input: func() proto.Message {
1008 m := &pb2.Extensions{
1009 OptString: proto.String("non-extension field"),
1010 OptBool: proto.Bool(true),
1011 OptInt32: proto.Int32(42),
1012 }
1013 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1014 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"})
1015 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{
1016 &pb2.Nested{OptString: proto.String("one")},
1017 &pb2.Nested{OptString: proto.String("two")},
1018 &pb2.Nested{OptString: proto.String("three")},
1019 })
1020 return m
1021 }(),
1022 want: `{
1023 "optString": "non-extension field",
1024 "optBool": true,
1025 "optInt32": 42,
1026 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
1027 "TEN",
1028 101,
1029 "ONE"
1030 ],
1031 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1032 {
1033 "optString": "one"
1034 },
1035 {
1036 "optString": "two"
1037 },
1038 {
1039 "optString": "three"
1040 }
1041 ],
1042 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1043 "hello",
1044 "world"
1045 ]
1046 }`,
1047 }, {
1048 desc: "MessageSet",
1049 input: func() proto.Message {
1050 m := &pb2.MessageSet{}
1051 proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1052 OptString: proto.String("a messageset extension"),
1053 })
1054 proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1055 OptString: proto.String("not a messageset extension"),
1056 })
1057 proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1058 OptString: proto.String("just a regular extension"),
1059 })
1060 return m
1061 }(),
1062 want: `{
1063 "[pb2.MessageSetExtension.ext_nested]": {
1064 "optString": "just a regular extension"
1065 },
1066 "[pb2.MessageSetExtension]": {
1067 "optString": "a messageset extension"
1068 },
1069 "[pb2.MessageSetExtension.not_message_set_extension]": {
1070 "optString": "not a messageset extension"
1071 }
1072 }`,
1073 skip: !flags.ProtoLegacy,
1074 }, {
1075 desc: "not real MessageSet 1",
1076 input: func() proto.Message {
1077 m := &pb2.FakeMessageSet{}
1078 proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1079 OptString: proto.String("not a messageset extension"),
1080 })
1081 return m
1082 }(),
1083 want: `{
1084 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1085 "optString": "not a messageset extension"
1086 }
1087 }`,
1088 skip: !flags.ProtoLegacy,
1089 }, {
1090 desc: "not real MessageSet 2",
1091 input: func() proto.Message {
1092 m := &pb2.MessageSet{}
1093 proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1094 OptString: proto.String("another not a messageset extension"),
1095 })
1096 return m
1097 }(),
1098 want: `{
1099 "[pb2.message_set_extension]": {
1100 "optString": "another not a messageset extension"
1101 }
1102 }`,
1103 skip: !flags.ProtoLegacy,
1104 }, {
1105 desc: "BoolValue empty",
1106 input: &wrapperspb.BoolValue{},
1107 want: `false`,
1108 }, {
1109 desc: "BoolValue",
1110 input: &wrapperspb.BoolValue{Value: true},
1111 want: `true`,
1112 }, {
1113 desc: "Int32Value empty",
1114 input: &wrapperspb.Int32Value{},
1115 want: `0`,
1116 }, {
1117 desc: "Int32Value",
1118 input: &wrapperspb.Int32Value{Value: 42},
1119 want: `42`,
1120 }, {
1121 desc: "Int64Value",
1122 input: &wrapperspb.Int64Value{Value: 42},
1123 want: `"42"`,
1124 }, {
1125 desc: "UInt32Value",
1126 input: &wrapperspb.UInt32Value{Value: 42},
1127 want: `42`,
1128 }, {
1129 desc: "UInt64Value",
1130 input: &wrapperspb.UInt64Value{Value: 42},
1131 want: `"42"`,
1132 }, {
1133 desc: "FloatValue",
1134 input: &wrapperspb.FloatValue{Value: 1.02},
1135 want: `1.02`,
1136 }, {
1137 desc: "FloatValue Infinity",
1138 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
1139 want: `"-Infinity"`,
1140 }, {
1141 desc: "DoubleValue",
1142 input: &wrapperspb.DoubleValue{Value: 1.02},
1143 want: `1.02`,
1144 }, {
1145 desc: "DoubleValue NaN",
1146 input: &wrapperspb.DoubleValue{Value: math.NaN()},
1147 want: `"NaN"`,
1148 }, {
1149 desc: "StringValue empty",
1150 input: &wrapperspb.StringValue{},
1151 want: `""`,
1152 }, {
1153 desc: "StringValue",
1154 input: &wrapperspb.StringValue{Value: "谷歌"},
1155 want: `"谷歌"`,
1156 }, {
1157 desc: "StringValue with invalid UTF8 error",
1158 input: &wrapperspb.StringValue{Value: "abc\xff"},
1159 wantErr: true,
1160 }, {
1161 desc: "StringValue field with invalid UTF8 error",
1162 input: &pb2.KnownTypes{
1163 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
1164 },
1165 wantErr: true,
1166 }, {
1167 desc: "BytesValue",
1168 input: &wrapperspb.BytesValue{Value: []byte("hello")},
1169 want: `"aGVsbG8="`,
1170 }, {
1171 desc: "Empty",
1172 input: &emptypb.Empty{},
1173 want: `{}`,
1174 }, {
1175 desc: "NullValue field",
1176 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
1177 want: `{
1178 "optNull": null
1179 }`,
1180 }, {
1181 desc: "Value empty",
1182 input: &structpb.Value{},
1183 wantErr: true,
1184 }, {
1185 desc: "Value empty field",
1186 input: &pb2.KnownTypes{
1187 OptValue: &structpb.Value{},
1188 },
1189 wantErr: true,
1190 }, {
1191 desc: "Value contains NullValue",
1192 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
1193 want: `null`,
1194 }, {
1195 desc: "Value contains BoolValue",
1196 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
1197 want: `false`,
1198 }, {
1199 desc: "Value contains NumberValue",
1200 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
1201 want: `1.02`,
1202 }, {
1203 desc: "Value contains StringValue",
1204 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
1205 want: `"hello"`,
1206 }, {
1207 desc: "Value contains StringValue with invalid UTF8",
1208 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
1209 wantErr: true,
1210 }, {
1211 desc: "Value contains Struct",
1212 input: &structpb.Value{
1213 Kind: &structpb.Value_StructValue{
1214 &structpb.Struct{
1215 Fields: map[string]*structpb.Value{
1216 "null": {Kind: &structpb.Value_NullValue{}},
1217 "number": {Kind: &structpb.Value_NumberValue{}},
1218 "string": {Kind: &structpb.Value_StringValue{}},
1219 "struct": {Kind: &structpb.Value_StructValue{}},
1220 "list": {Kind: &structpb.Value_ListValue{}},
1221 "bool": {Kind: &structpb.Value_BoolValue{}},
1222 },
1223 },
1224 },
1225 },
1226 want: `{
1227 "bool": false,
1228 "list": [],
1229 "null": null,
1230 "number": 0,
1231 "string": "",
1232 "struct": {}
1233 }`,
1234 }, {
1235 desc: "Value contains ListValue",
1236 input: &structpb.Value{
1237 Kind: &structpb.Value_ListValue{
1238 &structpb.ListValue{
1239 Values: []*structpb.Value{
1240 {Kind: &structpb.Value_BoolValue{}},
1241 {Kind: &structpb.Value_NullValue{}},
1242 {Kind: &structpb.Value_NumberValue{}},
1243 {Kind: &structpb.Value_StringValue{}},
1244 {Kind: &structpb.Value_StructValue{}},
1245 {Kind: &structpb.Value_ListValue{}},
1246 },
1247 },
1248 },
1249 },
1250 want: `[
1251 false,
1252 null,
1253 0,
1254 "",
1255 {},
1256 []
1257 ]`,
1258 }, {
1259 desc: "Value with NaN",
1260 input: structpb.NewNumberValue(math.NaN()),
1261 wantErr: true,
1262 }, {
1263 desc: "Value with -Inf",
1264 input: structpb.NewNumberValue(math.Inf(-1)),
1265 wantErr: true,
1266 }, {
1267 desc: "Value with +Inf",
1268 input: structpb.NewNumberValue(math.Inf(+1)),
1269 wantErr: true,
1270 }, {
1271 desc: "Struct with nil map",
1272 input: &structpb.Struct{},
1273 want: `{}`,
1274 }, {
1275 desc: "Struct with empty map",
1276 input: &structpb.Struct{
1277 Fields: map[string]*structpb.Value{},
1278 },
1279 want: `{}`,
1280 }, {
1281 desc: "Struct",
1282 input: &structpb.Struct{
1283 Fields: map[string]*structpb.Value{
1284 "bool": {Kind: &structpb.Value_BoolValue{true}},
1285 "null": {Kind: &structpb.Value_NullValue{}},
1286 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1287 "string": {Kind: &structpb.Value_StringValue{"hello"}},
1288 "struct": {
1289 Kind: &structpb.Value_StructValue{
1290 &structpb.Struct{
1291 Fields: map[string]*structpb.Value{
1292 "string": {Kind: &structpb.Value_StringValue{"world"}},
1293 },
1294 },
1295 },
1296 },
1297 "list": {
1298 Kind: &structpb.Value_ListValue{
1299 &structpb.ListValue{
1300 Values: []*structpb.Value{
1301 {Kind: &structpb.Value_BoolValue{}},
1302 {Kind: &structpb.Value_NullValue{}},
1303 {Kind: &structpb.Value_NumberValue{}},
1304 },
1305 },
1306 },
1307 },
1308 },
1309 },
1310 want: `{
1311 "bool": true,
1312 "list": [
1313 false,
1314 null,
1315 0
1316 ],
1317 "null": null,
1318 "number": 3.1415,
1319 "string": "hello",
1320 "struct": {
1321 "string": "world"
1322 }
1323 }`,
1324 }, {
1325 desc: "Struct message with invalid UTF8 string",
1326 input: &structpb.Struct{
1327 Fields: map[string]*structpb.Value{
1328 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
1329 },
1330 },
1331 wantErr: true,
1332 }, {
1333 desc: "ListValue with nil values",
1334 input: &structpb.ListValue{},
1335 want: `[]`,
1336 }, {
1337 desc: "ListValue with empty values",
1338 input: &structpb.ListValue{
1339 Values: []*structpb.Value{},
1340 },
1341 want: `[]`,
1342 }, {
1343 desc: "ListValue",
1344 input: &structpb.ListValue{
1345 Values: []*structpb.Value{
1346 {Kind: &structpb.Value_BoolValue{true}},
1347 {Kind: &structpb.Value_NullValue{}},
1348 {Kind: &structpb.Value_NumberValue{3.1415}},
1349 {Kind: &structpb.Value_StringValue{"hello"}},
1350 {
1351 Kind: &structpb.Value_ListValue{
1352 &structpb.ListValue{
1353 Values: []*structpb.Value{
1354 {Kind: &structpb.Value_BoolValue{}},
1355 {Kind: &structpb.Value_NullValue{}},
1356 {Kind: &structpb.Value_NumberValue{}},
1357 },
1358 },
1359 },
1360 },
1361 {
1362 Kind: &structpb.Value_StructValue{
1363 &structpb.Struct{
1364 Fields: map[string]*structpb.Value{
1365 "string": {Kind: &structpb.Value_StringValue{"world"}},
1366 },
1367 },
1368 },
1369 },
1370 },
1371 },
1372 want: `[
1373 true,
1374 null,
1375 3.1415,
1376 "hello",
1377 [
1378 false,
1379 null,
1380 0
1381 ],
1382 {
1383 "string": "world"
1384 }
1385 ]`,
1386 }, {
1387 desc: "ListValue with invalid UTF8 string",
1388 input: &structpb.ListValue{
1389 Values: []*structpb.Value{
1390 {Kind: &structpb.Value_StringValue{"\xff"}},
1391 },
1392 },
1393 wantErr: true,
1394 }, {
1395 desc: "Duration empty",
1396 input: &durationpb.Duration{},
1397 want: `"0s"`,
1398 }, {
1399 desc: "Duration with secs",
1400 input: &durationpb.Duration{Seconds: 3},
1401 want: `"3s"`,
1402 }, {
1403 desc: "Duration with -secs",
1404 input: &durationpb.Duration{Seconds: -3},
1405 want: `"-3s"`,
1406 }, {
1407 desc: "Duration with nanos",
1408 input: &durationpb.Duration{Nanos: 1e6},
1409 want: `"0.001s"`,
1410 }, {
1411 desc: "Duration with -nanos",
1412 input: &durationpb.Duration{Nanos: -1e6},
1413 want: `"-0.001s"`,
1414 }, {
1415 desc: "Duration with large secs",
1416 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
1417 want: `"10000000000.000000001s"`,
1418 }, {
1419 desc: "Duration with 6-digit nanos",
1420 input: &durationpb.Duration{Nanos: 1e4},
1421 want: `"0.000010s"`,
1422 }, {
1423 desc: "Duration with 3-digit nanos",
1424 input: &durationpb.Duration{Nanos: 1e6},
1425 want: `"0.001s"`,
1426 }, {
1427 desc: "Duration with -secs -nanos",
1428 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
1429 want: `"-123.000000450s"`,
1430 }, {
1431 desc: "Duration max value",
1432 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
1433 want: `"315576000000.999999999s"`,
1434 }, {
1435 desc: "Duration min value",
1436 input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
1437 want: `"-315576000000.999999999s"`,
1438 }, {
1439 desc: "Duration with +secs -nanos",
1440 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
1441 wantErr: true,
1442 }, {
1443 desc: "Duration with -secs +nanos",
1444 input: &durationpb.Duration{Seconds: -1, Nanos: 1},
1445 wantErr: true,
1446 }, {
1447 desc: "Duration with +secs out of range",
1448 input: &durationpb.Duration{Seconds: 315576000001},
1449 wantErr: true,
1450 }, {
1451 desc: "Duration with -secs out of range",
1452 input: &durationpb.Duration{Seconds: -315576000001},
1453 wantErr: true,
1454 }, {
1455 desc: "Duration with +nanos out of range",
1456 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
1457 wantErr: true,
1458 }, {
1459 desc: "Duration with -nanos out of range",
1460 input: &durationpb.Duration{Seconds: 0, Nanos: -1e9},
1461 wantErr: true,
1462 }, {
1463 desc: "Timestamp zero",
1464 input: ×tamppb.Timestamp{},
1465 want: `"1970-01-01T00:00:00Z"`,
1466 }, {
1467 desc: "Timestamp",
1468 input: ×tamppb.Timestamp{Seconds: 1553036601},
1469 want: `"2019-03-19T23:03:21Z"`,
1470 }, {
1471 desc: "Timestamp with nanos",
1472 input: ×tamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
1473 want: `"2019-03-19T23:03:21.000000001Z"`,
1474 }, {
1475 desc: "Timestamp with 6-digit nanos",
1476 input: ×tamppb.Timestamp{Nanos: 1e3},
1477 want: `"1970-01-01T00:00:00.000001Z"`,
1478 }, {
1479 desc: "Timestamp with 3-digit nanos",
1480 input: ×tamppb.Timestamp{Nanos: 1e7},
1481 want: `"1970-01-01T00:00:00.010Z"`,
1482 }, {
1483 desc: "Timestamp max value",
1484 input: ×tamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
1485 want: `"9999-12-31T23:59:59.999999999Z"`,
1486 }, {
1487 desc: "Timestamp min value",
1488 input: ×tamppb.Timestamp{Seconds: -62135596800},
1489 want: `"0001-01-01T00:00:00Z"`,
1490 }, {
1491 desc: "Timestamp with +secs out of range",
1492 input: ×tamppb.Timestamp{Seconds: 253402300800},
1493 wantErr: true,
1494 }, {
1495 desc: "Timestamp with -secs out of range",
1496 input: ×tamppb.Timestamp{Seconds: -62135596801},
1497 wantErr: true,
1498 }, {
1499 desc: "Timestamp with -nanos",
1500 input: ×tamppb.Timestamp{Nanos: -1},
1501 wantErr: true,
1502 }, {
1503 desc: "Timestamp with +nanos out of range",
1504 input: ×tamppb.Timestamp{Nanos: 1e9},
1505 wantErr: true,
1506 }, {
1507 desc: "FieldMask empty",
1508 input: &fieldmaskpb.FieldMask{},
1509 want: `""`,
1510 }, {
1511 desc: "FieldMask",
1512 input: &fieldmaskpb.FieldMask{
1513 Paths: []string{
1514 "foo",
1515 "foo_bar",
1516 "foo.bar_qux",
1517 "_foo",
1518 },
1519 },
1520 want: `"foo,fooBar,foo.barQux,Foo"`,
1521 }, {
1522 desc: "FieldMask empty string path",
1523 input: &fieldmaskpb.FieldMask{
1524 Paths: []string{""},
1525 },
1526 wantErr: true,
1527 }, {
1528 desc: "FieldMask path contains spaces only",
1529 input: &fieldmaskpb.FieldMask{
1530 Paths: []string{" "},
1531 },
1532 wantErr: true,
1533 }, {
1534 desc: "FieldMask irreversible error 1",
1535 input: &fieldmaskpb.FieldMask{
1536 Paths: []string{"foo_"},
1537 },
1538 wantErr: true,
1539 }, {
1540 desc: "FieldMask irreversible error 2",
1541 input: &fieldmaskpb.FieldMask{
1542 Paths: []string{"foo__bar"},
1543 },
1544 wantErr: true,
1545 }, {
1546 desc: "FieldMask invalid char",
1547 input: &fieldmaskpb.FieldMask{
1548 Paths: []string{"foo@bar"},
1549 },
1550 wantErr: true,
1551 }, {
1552 desc: "Any empty",
1553 input: &anypb.Any{},
1554 want: `{}`,
1555 }, {
1556 desc: "Any with non-custom message",
1557 input: func() proto.Message {
1558 m := &pb2.Nested{
1559 OptString: proto.String("embedded inside Any"),
1560 OptNested: &pb2.Nested{
1561 OptString: proto.String("inception"),
1562 },
1563 }
1564 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1565 if err != nil {
1566 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1567 }
1568 return &anypb.Any{
1569 TypeUrl: "foo/pb2.Nested",
1570 Value: b,
1571 }
1572 }(),
1573 want: `{
1574 "@type": "foo/pb2.Nested",
1575 "optString": "embedded inside Any",
1576 "optNested": {
1577 "optString": "inception"
1578 }
1579 }`,
1580 }, {
1581 desc: "Any with empty embedded message",
1582 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
1583 want: `{
1584 "@type": "foo/pb2.Nested"
1585 }`,
1586 }, {
1587 desc: "Any without registered type",
1588 mo: protojson.MarshalOptions{Resolver: new(protoregistry.Types)},
1589 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
1590 wantErr: true,
1591 }, {
1592 desc: "Any with missing required",
1593 input: func() proto.Message {
1594 m := &pb2.PartialRequired{
1595 OptString: proto.String("embedded inside Any"),
1596 }
1597 b, err := proto.MarshalOptions{
1598 AllowPartial: true,
1599 Deterministic: true,
1600 }.Marshal(m)
1601 if err != nil {
1602 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1603 }
1604 return &anypb.Any{
1605 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
1606 Value: b,
1607 }
1608 }(),
1609 want: `{
1610 "@type": "pb2.PartialRequired",
1611 "optString": "embedded inside Any"
1612 }`,
1613 }, {
1614 desc: "Any with partial required and AllowPartial",
1615 mo: protojson.MarshalOptions{
1616 AllowPartial: true,
1617 },
1618 input: func() proto.Message {
1619 m := &pb2.PartialRequired{
1620 OptString: proto.String("embedded inside Any"),
1621 }
1622 b, err := proto.MarshalOptions{
1623 AllowPartial: true,
1624 Deterministic: true,
1625 }.Marshal(m)
1626 if err != nil {
1627 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1628 }
1629 return &anypb.Any{
1630 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
1631 Value: b,
1632 }
1633 }(),
1634 want: `{
1635 "@type": "pb2.PartialRequired",
1636 "optString": "embedded inside Any"
1637 }`,
1638 }, {
1639 desc: "Any with EmitUnpopulated",
1640 mo: protojson.MarshalOptions{
1641 EmitUnpopulated: true,
1642 },
1643 input: func() proto.Message {
1644 return &anypb.Any{
1645 TypeUrl: string(new(pb3.Scalars).ProtoReflect().Descriptor().FullName()),
1646 }
1647 }(),
1648 want: `{
1649 "@type": "pb3.Scalars",
1650 "sBool": false,
1651 "sInt32": 0,
1652 "sInt64": "0",
1653 "sUint32": 0,
1654 "sUint64": "0",
1655 "sSint32": 0,
1656 "sSint64": "0",
1657 "sFixed32": 0,
1658 "sFixed64": "0",
1659 "sSfixed32": 0,
1660 "sSfixed64": "0",
1661 "sFloat": 0,
1662 "sDouble": 0,
1663 "sBytes": "",
1664 "sString": ""
1665 }`,
1666 }, {
1667 desc: "Any with invalid UTF8",
1668 input: func() proto.Message {
1669 m := &pb2.Nested{
1670 OptString: proto.String("abc\xff"),
1671 }
1672 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1673 if err != nil {
1674 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1675 }
1676 return &anypb.Any{
1677 TypeUrl: "foo/pb2.Nested",
1678 Value: b,
1679 }
1680 }(),
1681 wantErr: true,
1682 }, {
1683 desc: "Any with invalid value",
1684 input: &anypb.Any{
1685 TypeUrl: "foo/pb2.Nested",
1686 Value: []byte("\x80"),
1687 },
1688 wantErr: true,
1689 }, {
1690 desc: "Any with BoolValue",
1691 input: func() proto.Message {
1692 m := &wrapperspb.BoolValue{Value: true}
1693 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1694 if err != nil {
1695 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1696 }
1697 return &anypb.Any{
1698 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1699 Value: b,
1700 }
1701 }(),
1702 want: `{
1703 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1704 "value": true
1705 }`,
1706 }, {
1707 desc: "Any with Empty",
1708 input: func() proto.Message {
1709 m := &emptypb.Empty{}
1710 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1711 if err != nil {
1712 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1713 }
1714 return &anypb.Any{
1715 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1716 Value: b,
1717 }
1718 }(),
1719 want: `{
1720 "@type": "type.googleapis.com/google.protobuf.Empty",
1721 "value": {}
1722 }`,
1723 }, {
1724 desc: "Any with StringValue containing invalid UTF8",
1725 input: func() proto.Message {
1726 m := &wrapperspb.StringValue{Value: "abcd"}
1727 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1728 if err != nil {
1729 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1730 }
1731 return &anypb.Any{
1732 TypeUrl: "google.protobuf.StringValue",
1733 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
1734 }
1735 }(),
1736 wantErr: true,
1737 }, {
1738 desc: "Any with Int64Value",
1739 input: func() proto.Message {
1740 m := &wrapperspb.Int64Value{Value: 42}
1741 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1742 if err != nil {
1743 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1744 }
1745 return &anypb.Any{
1746 TypeUrl: "google.protobuf.Int64Value",
1747 Value: b,
1748 }
1749 }(),
1750 want: `{
1751 "@type": "google.protobuf.Int64Value",
1752 "value": "42"
1753 }`,
1754 }, {
1755 desc: "Any with Duration",
1756 input: func() proto.Message {
1757 m := &durationpb.Duration{}
1758 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1759 if err != nil {
1760 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1761 }
1762 return &anypb.Any{
1763 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1764 Value: b,
1765 }
1766 }(),
1767 want: `{
1768 "@type": "type.googleapis.com/google.protobuf.Duration",
1769 "value": "0s"
1770 }`,
1771 }, {
1772 desc: "Any with empty Value",
1773 input: func() proto.Message {
1774 m := &structpb.Value{}
1775 b, err := proto.Marshal(m)
1776 if err != nil {
1777 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1778 }
1779 return &anypb.Any{
1780 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1781 Value: b,
1782 }
1783 }(),
1784 wantErr: true,
1785 }, {
1786 desc: "Any with Value of StringValue",
1787 input: func() proto.Message {
1788 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
1789 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1790 if err != nil {
1791 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1792 }
1793 return &anypb.Any{
1794 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1795 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
1796 }
1797 }(),
1798 wantErr: true,
1799 }, {
1800 desc: "Any with Value of NullValue",
1801 input: func() proto.Message {
1802 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
1803 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1804 if err != nil {
1805 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1806 }
1807 return &anypb.Any{
1808 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1809 Value: b,
1810 }
1811 }(),
1812 want: `{
1813 "@type": "type.googleapis.com/google.protobuf.Value",
1814 "value": null
1815 }`,
1816 }, {
1817 desc: "Any with Struct",
1818 input: func() proto.Message {
1819 m := &structpb.Struct{
1820 Fields: map[string]*structpb.Value{
1821 "bool": {Kind: &structpb.Value_BoolValue{true}},
1822 "null": {Kind: &structpb.Value_NullValue{}},
1823 "string": {Kind: &structpb.Value_StringValue{"hello"}},
1824 "struct": {
1825 Kind: &structpb.Value_StructValue{
1826 &structpb.Struct{
1827 Fields: map[string]*structpb.Value{
1828 "string": {Kind: &structpb.Value_StringValue{"world"}},
1829 },
1830 },
1831 },
1832 },
1833 },
1834 }
1835 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1836 if err != nil {
1837 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1838 }
1839 return &anypb.Any{
1840 TypeUrl: "google.protobuf.Struct",
1841 Value: b,
1842 }
1843 }(),
1844 want: `{
1845 "@type": "google.protobuf.Struct",
1846 "value": {
1847 "bool": true,
1848 "null": null,
1849 "string": "hello",
1850 "struct": {
1851 "string": "world"
1852 }
1853 }
1854 }`,
1855 }, {
1856 desc: "Any with missing type_url",
1857 input: func() proto.Message {
1858 m := &wrapperspb.BoolValue{Value: true}
1859 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1860 if err != nil {
1861 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1862 }
1863 return &anypb.Any{
1864 Value: b,
1865 }
1866 }(),
1867 wantErr: true,
1868 }, {
1869 desc: "well known types as field values",
1870 input: &pb2.KnownTypes{
1871 OptBool: &wrapperspb.BoolValue{Value: false},
1872 OptInt32: &wrapperspb.Int32Value{Value: 42},
1873 OptInt64: &wrapperspb.Int64Value{Value: 42},
1874 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1875 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1876 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1877 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1878 OptString: &wrapperspb.StringValue{Value: "hello"},
1879 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1880 OptDuration: &durationpb.Duration{Seconds: 123},
1881 OptTimestamp: ×tamppb.Timestamp{Seconds: 1553036601},
1882 OptStruct: &structpb.Struct{
1883 Fields: map[string]*structpb.Value{
1884 "string": {Kind: &structpb.Value_StringValue{"hello"}},
1885 },
1886 },
1887 OptList: &structpb.ListValue{
1888 Values: []*structpb.Value{
1889 {Kind: &structpb.Value_NullValue{}},
1890 {Kind: &structpb.Value_StringValue{}},
1891 {Kind: &structpb.Value_StructValue{}},
1892 {Kind: &structpb.Value_ListValue{}},
1893 },
1894 },
1895 OptValue: &structpb.Value{
1896 Kind: &structpb.Value_StringValue{"world"},
1897 },
1898 OptEmpty: &emptypb.Empty{},
1899 OptAny: &anypb.Any{
1900 TypeUrl: "google.protobuf.Empty",
1901 },
1902 OptFieldmask: &fieldmaskpb.FieldMask{
1903 Paths: []string{"foo_bar", "bar_foo"},
1904 },
1905 },
1906 want: `{
1907 "optBool": false,
1908 "optInt32": 42,
1909 "optInt64": "42",
1910 "optUint32": 42,
1911 "optUint64": "42",
1912 "optFloat": 1.23,
1913 "optDouble": 3.1415,
1914 "optString": "hello",
1915 "optBytes": "aGVsbG8=",
1916 "optDuration": "123s",
1917 "optTimestamp": "2019-03-19T23:03:21Z",
1918 "optStruct": {
1919 "string": "hello"
1920 },
1921 "optList": [
1922 null,
1923 "",
1924 {},
1925 []
1926 ],
1927 "optValue": "world",
1928 "optEmpty": {},
1929 "optAny": {
1930 "@type": "google.protobuf.Empty",
1931 "value": {}
1932 },
1933 "optFieldmask": "fooBar,barFoo"
1934 }`,
1935 }, {
1936 desc: "EmitUnpopulated: proto2 optional scalars",
1937 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1938 input: &pb2.Scalars{},
1939 want: `{
1940 "optBool": null,
1941 "optInt32": null,
1942 "optInt64": null,
1943 "optUint32": null,
1944 "optUint64": null,
1945 "optSint32": null,
1946 "optSint64": null,
1947 "optFixed32": null,
1948 "optFixed64": null,
1949 "optSfixed32": null,
1950 "optSfixed64": null,
1951 "optFloat": null,
1952 "optDouble": null,
1953 "optBytes": null,
1954 "optString": null
1955 }`,
1956 }, {
1957 desc: "EmitUnpopulated: proto3 scalars",
1958 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1959 input: &pb3.Scalars{},
1960 want: `{
1961 "sBool": false,
1962 "sInt32": 0,
1963 "sInt64": "0",
1964 "sUint32": 0,
1965 "sUint64": "0",
1966 "sSint32": 0,
1967 "sSint64": "0",
1968 "sFixed32": 0,
1969 "sFixed64": "0",
1970 "sSfixed32": 0,
1971 "sSfixed64": "0",
1972 "sFloat": 0,
1973 "sDouble": 0,
1974 "sBytes": "",
1975 "sString": ""
1976 }`,
1977 }, {
1978 desc: "EmitUnpopulated: proto2 enum",
1979 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1980 input: &pb2.Enums{},
1981 want: `{
1982 "optEnum": null,
1983 "rptEnum": [],
1984 "optNestedEnum": null,
1985 "rptNestedEnum": []
1986 }`,
1987 }, {
1988 desc: "EmitUnpopulated: proto3 enum",
1989 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1990 input: &pb3.Enums{},
1991 want: `{
1992 "sEnum": "ZERO",
1993 "sNestedEnum": "CERO"
1994 }`,
1995 }, {
1996 desc: "EmitUnpopulated: proto2 message and group fields",
1997 mo: protojson.MarshalOptions{EmitUnpopulated: true},
1998 input: &pb2.Nests{},
1999 want: `{
2000 "optNested": null,
2001 "optgroup": null,
2002 "rptNested": [],
2003 "rptgroup": []
2004 }`,
2005 }, {
2006 desc: "EmitUnpopulated: proto3 message field",
2007 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2008 input: &pb3.Nests{},
2009 want: `{
2010 "sNested": null
2011 }`,
2012 }, {
2013 desc: "EmitUnpopulated: proto2 empty message and group fields",
2014 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2015 input: &pb2.Nests{
2016 OptNested: &pb2.Nested{},
2017 Optgroup: &pb2.Nests_OptGroup{},
2018 },
2019 want: `{
2020 "optNested": {
2021 "optString": null,
2022 "optNested": null
2023 },
2024 "optgroup": {
2025 "optString": null,
2026 "optNested": null,
2027 "optnestedgroup": null
2028 },
2029 "rptNested": [],
2030 "rptgroup": []
2031 }`,
2032 }, {
2033 desc: "EmitUnpopulated: proto3 empty message field",
2034 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2035 input: &pb3.Nests{
2036 SNested: &pb3.Nested{},
2037 },
2038 want: `{
2039 "sNested": {
2040 "sString": "",
2041 "sNested": null
2042 }
2043 }`,
2044 }, {
2045 desc: "EmitUnpopulated: proto2 required fields",
2046 mo: protojson.MarshalOptions{
2047 AllowPartial: true,
2048 EmitUnpopulated: true,
2049 },
2050 input: &pb2.Requireds{},
2051 want: `{
2052 "reqBool": null,
2053 "reqSfixed64": null,
2054 "reqDouble": null,
2055 "reqString": null,
2056 "reqEnum": null,
2057 "reqNested": null
2058 }`,
2059 }, {
2060 desc: "EmitUnpopulated: repeated fields",
2061 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2062 input: &pb2.Repeats{},
2063 want: `{
2064 "rptBool": [],
2065 "rptInt32": [],
2066 "rptInt64": [],
2067 "rptUint32": [],
2068 "rptUint64": [],
2069 "rptFloat": [],
2070 "rptDouble": [],
2071 "rptString": [],
2072 "rptBytes": []
2073 }`,
2074 }, {
2075 desc: "EmitUnpopulated: repeated containing empty message",
2076 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2077 input: &pb2.Nests{
2078 RptNested: []*pb2.Nested{nil, {}},
2079 },
2080 want: `{
2081 "optNested": null,
2082 "optgroup": null,
2083 "rptNested": [
2084 {
2085 "optString": null,
2086 "optNested": null
2087 },
2088 {
2089 "optString": null,
2090 "optNested": null
2091 }
2092 ],
2093 "rptgroup": []
2094 }`,
2095 }, {
2096 desc: "EmitUnpopulated: map fields",
2097 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2098 input: &pb3.Maps{},
2099 want: `{
2100 "int32ToStr": {},
2101 "boolToUint32": {},
2102 "uint64ToEnum": {},
2103 "strToNested": {},
2104 "strToOneofs": {}
2105 }`,
2106 }, {
2107 desc: "EmitUnpopulated: map containing empty message",
2108 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2109 input: &pb3.Maps{
2110 StrToNested: map[string]*pb3.Nested{
2111 "nested": &pb3.Nested{},
2112 },
2113 StrToOneofs: map[string]*pb3.Oneofs{
2114 "nested": &pb3.Oneofs{},
2115 },
2116 },
2117 want: `{
2118 "int32ToStr": {},
2119 "boolToUint32": {},
2120 "uint64ToEnum": {},
2121 "strToNested": {
2122 "nested": {
2123 "sString": "",
2124 "sNested": null
2125 }
2126 },
2127 "strToOneofs": {
2128 "nested": {}
2129 }
2130 }`,
2131 }, {
2132 desc: "EmitUnpopulated: oneof fields",
2133 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2134 input: &pb3.Oneofs{},
2135 want: `{}`,
2136 }, {
2137 desc: "EmitUnpopulated: extensions",
2138 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2139 input: func() proto.Message {
2140 m := &pb2.Extensions{}
2141 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{})
2142 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
2143 nil,
2144 {},
2145 })
2146 return m
2147 }(),
2148 want: `{
2149 "optString": null,
2150 "optBool": null,
2151 "optInt32": null,
2152 "[pb2.opt_ext_nested]": {
2153 "optString": null,
2154 "optNested": null
2155 },
2156 "[pb2.rpt_ext_nested]": [
2157 {
2158 "optString": null,
2159 "optNested": null
2160 },
2161 {
2162 "optString": null,
2163 "optNested": null
2164 }
2165 ]
2166 }`,
2167 }, {
2168 desc: "EmitUnpopulated: with populated fields",
2169 mo: protojson.MarshalOptions{EmitUnpopulated: true},
2170 input: &pb2.Scalars{
2171 OptInt32: proto.Int32(0xff),
2172 OptUint32: proto.Uint32(47),
2173 OptSint32: proto.Int32(-1001),
2174 OptFixed32: proto.Uint32(32),
2175 OptSfixed32: proto.Int32(-32),
2176 OptFloat: proto.Float32(1.02),
2177 OptBytes: []byte("谷歌"),
2178 },
2179 want: `{
2180 "optBool": null,
2181 "optInt32": 255,
2182 "optInt64": null,
2183 "optUint32": 47,
2184 "optUint64": null,
2185 "optSint32": -1001,
2186 "optSint64": null,
2187 "optFixed32": 32,
2188 "optFixed64": null,
2189 "optSfixed32": -32,
2190 "optSfixed64": null,
2191 "optFloat": 1.02,
2192 "optDouble": null,
2193 "optBytes": "6LC35q2M",
2194 "optString": null
2195 }`,
2196 }, {
2197 desc: "EmitUnpopulated overrides EmitDefaultValues",
2198 mo: protojson.MarshalOptions{EmitUnpopulated: true, EmitDefaultValues: true},
2199 input: &pb2.Nests{
2200 RptNested: []*pb2.Nested{nil, {}},
2201 },
2202 want: `{
2203 "optNested": null,
2204 "optgroup": null,
2205 "rptNested": [
2206 {
2207 "optString": null,
2208 "optNested": null
2209 },
2210 {
2211 "optString": null,
2212 "optNested": null
2213 }
2214 ],
2215 "rptgroup": []
2216 }`,
2217 }, {
2218 desc: "EmitDefaultValues: proto2 optional scalars",
2219 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2220 input: &pb2.Scalars{},
2221 want: `{}`,
2222 }, {
2223 desc: "EmitDefaultValues: proto3 scalars",
2224 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2225 input: &pb3.Scalars{},
2226 want: `{
2227 "sBool": false,
2228 "sInt32": 0,
2229 "sInt64": "0",
2230 "sUint32": 0,
2231 "sUint64": "0",
2232 "sSint32": 0,
2233 "sSint64": "0",
2234 "sFixed32": 0,
2235 "sFixed64": "0",
2236 "sSfixed32": 0,
2237 "sSfixed64": "0",
2238 "sFloat": 0,
2239 "sDouble": 0,
2240 "sBytes": "",
2241 "sString": ""
2242 }`,
2243 }, {
2244 desc: "EmitDefaultValues: proto2 enum",
2245 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2246 input: &pb2.Enums{},
2247 want: `{
2248 "rptEnum": [],
2249 "rptNestedEnum": []
2250 }`,
2251 }, {
2252 desc: "EmitDefaultValues: proto3 enum",
2253 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2254 input: &pb3.Enums{},
2255 want: `{
2256 "sEnum": "ZERO",
2257 "sNestedEnum": "CERO"
2258 }`,
2259 }, {
2260 desc: "EmitDefaultValues: proto2 message and group fields",
2261 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2262 input: &pb2.Nests{},
2263 want: `{
2264 "rptNested": [],
2265 "rptgroup": []
2266 }`,
2267 }, {
2268 desc: "EmitDefaultValues: proto3 message field",
2269 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2270 input: &pb3.Nests{},
2271 want: `{}`,
2272 }, {
2273 desc: "EmitDefaultValues: proto2 empty message and group fields",
2274 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2275 input: &pb2.Nests{
2276 OptNested: &pb2.Nested{},
2277 Optgroup: &pb2.Nests_OptGroup{},
2278 },
2279 want: `{
2280 "optNested": {},
2281 "optgroup": {},
2282 "rptNested": [],
2283 "rptgroup": []
2284 }`,
2285 }, {
2286 desc: "EmitDefaultValues: proto3 empty message field",
2287 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2288 input: &pb3.Nests{
2289 SNested: &pb3.Nested{},
2290 },
2291 want: `{
2292 "sNested": {
2293 "sString": ""
2294 }
2295 }`,
2296 }, {
2297 desc: "EmitDefaultValues: proto2 required fields",
2298 mo: protojson.MarshalOptions{
2299 AllowPartial: true,
2300 EmitDefaultValues: true,
2301 },
2302 input: &pb2.Requireds{},
2303 want: `{}`,
2304 }, {
2305 desc: "EmitDefaultValues: repeated fields",
2306 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2307 input: &pb2.Repeats{},
2308 want: `{
2309 "rptBool": [],
2310 "rptInt32": [],
2311 "rptInt64": [],
2312 "rptUint32": [],
2313 "rptUint64": [],
2314 "rptFloat": [],
2315 "rptDouble": [],
2316 "rptString": [],
2317 "rptBytes": []
2318 }`,
2319 }, {
2320 desc: "EmitDefaultValues: repeated containing empty message",
2321 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2322 input: &pb2.Nests{
2323 RptNested: []*pb2.Nested{nil, {}},
2324 },
2325 want: `{
2326 "rptNested": [
2327 {},
2328 {}
2329 ],
2330 "rptgroup": []
2331 }`,
2332 }, {
2333 desc: "EmitDefaultValues: map fields",
2334 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2335 input: &pb3.Maps{},
2336 want: `{
2337 "int32ToStr": {},
2338 "boolToUint32": {},
2339 "uint64ToEnum": {},
2340 "strToNested": {},
2341 "strToOneofs": {}
2342 }`,
2343 }, {
2344 desc: "EmitDefaultValues: map containing empty message",
2345 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2346 input: &pb3.Maps{
2347 StrToNested: map[string]*pb3.Nested{
2348 "nested": &pb3.Nested{},
2349 },
2350 StrToOneofs: map[string]*pb3.Oneofs{
2351 "nested": &pb3.Oneofs{},
2352 },
2353 },
2354 want: `{
2355 "int32ToStr": {},
2356 "boolToUint32": {},
2357 "uint64ToEnum": {},
2358 "strToNested": {
2359 "nested": {
2360 "sString": ""
2361 }
2362 },
2363 "strToOneofs": {
2364 "nested": {}
2365 }
2366 }`,
2367 }, {
2368 desc: "EmitDefaultValues: oneof fields",
2369 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2370 input: &pb3.Oneofs{},
2371 want: `{}`,
2372 }, {
2373 desc: "EmitDefaultValues: extensions",
2374 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2375 input: func() proto.Message {
2376 m := &pb2.Extensions{}
2377 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{})
2378 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
2379 nil,
2380 {},
2381 })
2382 return m
2383 }(),
2384 want: `{
2385 "[pb2.opt_ext_nested]": {},
2386 "[pb2.rpt_ext_nested]": [
2387 {},
2388 {}
2389 ]
2390 }`,
2391 }, {
2392 desc: "EmitDefaultValues: with populated fields",
2393 mo: protojson.MarshalOptions{EmitDefaultValues: true},
2394 input: &pb2.Scalars{
2395 OptInt32: proto.Int32(0xff),
2396 OptUint32: proto.Uint32(47),
2397 OptSint32: proto.Int32(-1001),
2398 OptFixed32: proto.Uint32(32),
2399 OptSfixed32: proto.Int32(-32),
2400 OptFloat: proto.Float32(1.02),
2401 OptBytes: []byte("谷歌"),
2402 },
2403 want: `{
2404 "optInt32": 255,
2405 "optUint32": 47,
2406 "optSint32": -1001,
2407 "optFixed32": 32,
2408 "optSfixed32": -32,
2409 "optFloat": 1.02,
2410 "optBytes": "6LC35q2M"
2411 }`,
2412 }, {
2413 desc: "UseEnumNumbers in singular field",
2414 mo: protojson.MarshalOptions{UseEnumNumbers: true},
2415 input: &pb2.Enums{
2416 OptEnum: pb2.Enum_ONE.Enum(),
2417 OptNestedEnum: pb2.Enums_UNO.Enum(),
2418 },
2419 want: `{
2420 "optEnum": 1,
2421 "optNestedEnum": 1
2422 }`,
2423 }, {
2424 desc: "UseEnumNumbers in repeated field",
2425 mo: protojson.MarshalOptions{UseEnumNumbers: true},
2426 input: &pb2.Enums{
2427 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
2428 RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_UNO, pb2.Enums_DOS, 47},
2429 },
2430 want: `{
2431 "rptEnum": [
2432 1,
2433 2,
2434 10,
2435 42
2436 ],
2437 "rptNestedEnum": [
2438 1,
2439 2,
2440 47
2441 ]
2442 }`,
2443 }, {
2444 desc: "UseEnumNumbers in map field",
2445 mo: protojson.MarshalOptions{UseEnumNumbers: true},
2446 input: &pb3.Maps{
2447 Uint64ToEnum: map[uint64]pb3.Enum{
2448 1: pb3.Enum_ONE,
2449 2: pb3.Enum_TWO,
2450 10: pb3.Enum_TEN,
2451 47: 47,
2452 },
2453 },
2454 want: `{
2455 "uint64ToEnum": {
2456 "1": 1,
2457 "2": 2,
2458 "10": 10,
2459 "47": 47
2460 }
2461 }`,
2462 }, {
2463 desc: "UseProtoNames",
2464 mo: protojson.MarshalOptions{UseProtoNames: true},
2465 input: &pb2.Nests{
2466 OptNested: &pb2.Nested{},
2467 Optgroup: &pb2.Nests_OptGroup{
2468 OptString: proto.String("inside a group"),
2469 OptNested: &pb2.Nested{
2470 OptString: proto.String("nested message inside a group"),
2471 },
2472 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
2473 OptFixed32: proto.Uint32(47),
2474 },
2475 },
2476 Rptgroup: []*pb2.Nests_RptGroup{
2477 {
2478 RptString: []string{"hello", "world"},
2479 },
2480 },
2481 },
2482 want: `{
2483 "opt_nested": {},
2484 "OptGroup": {
2485 "opt_string": "inside a group",
2486 "opt_nested": {
2487 "opt_string": "nested message inside a group"
2488 },
2489 "OptNestedGroup": {
2490 "opt_fixed32": 47
2491 }
2492 },
2493 "RptGroup": [
2494 {
2495 "rpt_string": [
2496 "hello",
2497 "world"
2498 ]
2499 }
2500 ]
2501 }`,
2502 }}
2503
2504 for _, tt := range tests {
2505 tt := tt
2506 if tt.skip {
2507 continue
2508 }
2509 t.Run(tt.desc, func(t *testing.T) {
2510
2511 tt.mo.Indent = " "
2512 b, err := tt.mo.Marshal(tt.input)
2513 if err != nil && !tt.wantErr {
2514 t.Errorf("Marshal() returned error: %v\n", err)
2515 }
2516 if err == nil && tt.wantErr {
2517 t.Errorf("Marshal() got nil error, want error\n")
2518 }
2519 got := string(b)
2520 if got != tt.want {
2521 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
2522 if diff := cmp.Diff(tt.want, got); diff != "" {
2523 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
2524 }
2525 }
2526 })
2527 }
2528 }
2529
2530 func TestEncodeAppend(t *testing.T) {
2531 want := []byte("prefix")
2532 got := append([]byte(nil), want...)
2533 got, err := protojson.MarshalOptions{}.MarshalAppend(got, &pb3.Scalars{
2534 SString: "value",
2535 })
2536 if err != nil {
2537 t.Fatal(err)
2538 }
2539 if !bytes.HasPrefix(got, want) {
2540 t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
2541 }
2542 }
2543
2544 func TestMarshalAppendAllocations(t *testing.T) {
2545 m := &pb3.Scalars{SInt32: 1}
2546 const count = 1000
2547 size := 12
2548 b := make([]byte, size)
2549
2550 marshalAllocs := testing.AllocsPerRun(count, func() {
2551 _, err := protojson.MarshalOptions{}.MarshalAppend(b[:0], m)
2552 if err != nil {
2553 t.Fatal(err)
2554 }
2555 })
2556 b = nil
2557 marshalAppendAllocs := testing.AllocsPerRun(count, func() {
2558 var err error
2559 b, err = protojson.MarshalOptions{}.MarshalAppend(b, m)
2560 if err != nil {
2561 t.Fatal(err)
2562 }
2563 })
2564 if marshalAllocs != marshalAppendAllocs {
2565 t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
2566 t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
2567 t.Errorf("expect amortized allocs/op to be identical")
2568 }
2569 }
2570
View as plain text