encoding: support weak fields in text and JSON unmarshaling
If the message for a weak field is linked in,
we treat it as if it were identical to a normal known field.
However, if the weak field is not linked in,
we treat it as if the field were not known.
Change-Id: I576d911deec98e13211304024a6353734d055465
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185457
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/encoding/protojson/decode.go b/encoding/protojson/decode.go
index 8dad1d5..d97a60c 100644
--- a/encoding/protojson/decode.go
+++ b/encoding/protojson/decode.go
@@ -186,6 +186,9 @@
if fd == nil {
fd = fieldDescs.ByName(pref.Name(name))
}
+ if fd != nil && fd.IsWeak() && fd.Message().IsPlaceholder() {
+ fd = nil // reset since the weak reference is not linked in
+ }
}
if fd == nil {
diff --git a/encoding/protojson/decode_test.go b/encoding/protojson/decode_test.go
index 00b1a54..03bd10a 100644
--- a/encoding/protojson/decode_test.go
+++ b/encoding/protojson/decode_test.go
@@ -9,12 +9,15 @@
"testing"
"google.golang.org/protobuf/encoding/protojson"
- "google.golang.org/protobuf/encoding/testprotos/pb2"
- "google.golang.org/protobuf/encoding/testprotos/pb3"
+ "google.golang.org/protobuf/internal/flags"
pimpl "google.golang.org/protobuf/internal/impl"
"google.golang.org/protobuf/proto"
preg "google.golang.org/protobuf/reflect/protoregistry"
+ "google.golang.org/protobuf/encoding/testprotos/pb2"
+ "google.golang.org/protobuf/encoding/testprotos/pb3"
+ testpb "google.golang.org/protobuf/internal/testprotos/test"
+ weakpb "google.golang.org/protobuf/internal/testprotos/test/weak1"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/emptypb"
@@ -33,6 +36,7 @@
wantMessage proto.Message
// TODO: verify expected error message substring.
wantErr bool
+ skip bool
}{{
desc: "proto2 empty message",
inputMessage: &pb2.Scalars{},
@@ -2440,10 +2444,29 @@
wantMessage: &anypb.Any{
TypeUrl: "type.googleapis.com/google.protobuf.Empty",
},
+ }, {
+ desc: "weak fields",
+ inputMessage: &testpb.TestWeak{},
+ inputText: `{"weak_message1":{"a":1}}`,
+ wantMessage: func() *testpb.TestWeak {
+ m := new(testpb.TestWeak)
+ m.SetWeakMessage1(&weakpb.WeakImportMessage1{A: proto.Int32(1)})
+ return m
+ }(),
+ skip: !flags.Proto1Legacy,
+ }, {
+ desc: "weak fields; unknown field",
+ inputMessage: &testpb.TestWeak{},
+ inputText: `{"weak_message1":{"a":1}, "weak_message2":{"a":1}}`,
+ wantErr: true, // weak_message2 is unknown since the package containing it is not imported
+ skip: !flags.Proto1Legacy,
}}
for _, tt := range tests {
tt := tt
+ if tt.skip {
+ continue
+ }
t.Run(tt.desc, func(t *testing.T) {
err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage)
if err != nil && !tt.wantErr {
diff --git a/encoding/prototext/decode.go b/encoding/prototext/decode.go
index df05f95..7874410 100644
--- a/encoding/prototext/decode.go
+++ b/encoding/prototext/decode.go
@@ -95,19 +95,19 @@
case text.Name:
name, _ = tkey.Name()
fd = fieldDescs.ByName(name)
- // The proto name of a group field is in all lowercase. However, the
- // textproto field name is the type name. Check to make sure that
- // group name is correct.
- if fd == nil {
+ switch {
+ case fd == nil:
+ // The proto name of a group field is in all lowercase,
+ // while the textproto field name is the group message name.
+ // Check to make sure that group name is correct.
gd := fieldDescs.ByName(pref.Name(strings.ToLower(string(name))))
if gd != nil && gd.Kind() == pref.GroupKind && gd.Message().Name() == name {
fd = gd
}
- } else {
- if fd.Kind() == pref.GroupKind && fd.Message().Name() != name {
- // Reset fd to nil because name does not match.
- fd = nil
- }
+ case fd.Kind() == pref.GroupKind && fd.Message().Name() != name:
+ fd = nil // reset since field name is actually the message name
+ case fd.IsWeak() && fd.Message().IsPlaceholder():
+ fd = nil // reset since the weak reference is not linked in
}
case text.String:
// Handle extensions only. This code path is not for Any.
diff --git a/encoding/prototext/decode_test.go b/encoding/prototext/decode_test.go
index 66c77f3..31de4d7 100644
--- a/encoding/prototext/decode_test.go
+++ b/encoding/prototext/decode_test.go
@@ -9,12 +9,15 @@
"testing"
"google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/internal/flags"
pimpl "google.golang.org/protobuf/internal/impl"
"google.golang.org/protobuf/proto"
preg "google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/encoding/testprotos/pb2"
"google.golang.org/protobuf/encoding/testprotos/pb3"
+ testpb "google.golang.org/protobuf/internal/testprotos/test"
+ weakpb "google.golang.org/protobuf/internal/testprotos/test/weak1"
"google.golang.org/protobuf/types/known/anypb"
)
@@ -26,6 +29,7 @@
inputText string
wantMessage proto.Message
wantErr bool // TODO: Verify error message content.
+ skip bool
}{{
desc: "proto2 empty message",
inputMessage: &pb2.Scalars{},
@@ -1484,10 +1488,29 @@
type_url: "pb2.Nested"
`,
wantErr: true,
+ }, {
+ desc: "weak fields",
+ inputMessage: &testpb.TestWeak{},
+ inputText: `weak_message1:{a:1}`,
+ wantMessage: func() *testpb.TestWeak {
+ m := new(testpb.TestWeak)
+ m.SetWeakMessage1(&weakpb.WeakImportMessage1{A: proto.Int32(1)})
+ return m
+ }(),
+ skip: !flags.Proto1Legacy,
+ }, {
+ desc: "weak fields; unknown field",
+ inputMessage: &testpb.TestWeak{},
+ inputText: `weak_message1:{a:1} weak_message2:{a:1}`,
+ wantErr: true, // weak_message2 is unknown since the package containing it is not imported
+ skip: !flags.Proto1Legacy,
}}
for _, tt := range tests {
tt := tt
+ if tt.skip {
+ continue
+ }
t.Run(tt.desc, func(t *testing.T) {
err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage)
if err != nil && !tt.wantErr {