encoding/prototext: add MarshalOptions.EmitUnknown
This changes text marshaling to avoid unknown fields by default
and instead adds an option so that unknown fields be emitted.
This ensures that the default marshal/unknown can round-trip.
Change-Id: I85c84ba6ab7916d538ec6bfd4e9d399a8fcba14e
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/195778
Reviewed-by: Herbie Ong <herbie@google.com>
diff --git a/encoding/prototext/encode.go b/encoding/prototext/encode.go
index 8856dae..eef63d3 100644
--- a/encoding/prototext/encode.go
+++ b/encoding/prototext/encode.go
@@ -38,6 +38,11 @@
// Marshal will return error if there are any missing required fields.
AllowPartial bool
+ // EmitUnknown specifies whether to emit unknown fields in the output.
+ // If specified, the unmarshaler may be unable to parse the output.
+ // The default is to exclude unknown fields.
+ EmitUnknown bool
+
// If Indent is a non-empty string, it causes entries for a Message to be
// preceded by the indent and trailed by a newline. Indent can only be
// composed of space or tab characters.
@@ -123,8 +128,9 @@
}
// Handle unknown fields.
- // TODO: Provide option to exclude or include unknown fields.
- msgFields = appendUnknown(msgFields, m.GetUnknown())
+ if o.EmitUnknown {
+ msgFields = appendUnknown(msgFields, m.GetUnknown())
+ }
return text.ValueOf(msgFields), nil
}
diff --git a/encoding/prototext/encode_test.go b/encoding/prototext/encode_test.go
index 839c4f9..68ceb45 100644
--- a/encoding/prototext/encode_test.go
+++ b/encoding/prototext/encode_test.go
@@ -821,7 +821,24 @@
},
want: "oneof_nested: {}\n",
}, {
+ desc: "unknown fields not printed",
+ input: func() proto.Message {
+ m := &pb2.Scalars{
+ OptString: proto.String("this message contains unknown fields"),
+ }
+ m.ProtoReflect().SetUnknown(pack.Message{
+ pack.Tag{101, pack.VarintType}, pack.Bool(true),
+ pack.Tag{102, pack.VarintType}, pack.Varint(0xff),
+ pack.Tag{103, pack.Fixed32Type}, pack.Uint32(47),
+ pack.Tag{104, pack.Fixed64Type}, pack.Int64(0xdeadbeef),
+ }.Marshal())
+ return m
+ }(),
+ want: `opt_string: "this message contains unknown fields"
+`,
+ }, {
desc: "unknown varint and fixed types",
+ mo: prototext.MarshalOptions{EmitUnknown: true},
input: func() proto.Message {
m := &pb2.Scalars{
OptString: proto.String("this message contains unknown fields"),
@@ -842,6 +859,7 @@
`,
}, {
desc: "unknown length-delimited",
+ mo: prototext.MarshalOptions{EmitUnknown: true},
input: func() proto.Message {
m := new(pb2.Scalars)
m.ProtoReflect().SetUnknown(pack.Message{
@@ -857,6 +875,7 @@
`,
}, {
desc: "unknown group type",
+ mo: prototext.MarshalOptions{EmitUnknown: true},
input: func() proto.Message {
m := new(pb2.Scalars)
m.ProtoReflect().SetUnknown(pack.Message{
@@ -876,6 +895,7 @@
`,
}, {
desc: "unknown unpack repeated field",
+ mo: prototext.MarshalOptions{EmitUnknown: true},
input: func() proto.Message {
m := new(pb2.Scalars)
m.ProtoReflect().SetUnknown(pack.Message{
diff --git a/internal/conformance/conformance_test.go b/internal/conformance/conformance_test.go
index c319651..1202050 100644
--- a/internal/conformance/conformance_test.go
+++ b/internal/conformance/conformance_test.go
@@ -143,7 +143,9 @@
},
}
case pb.WireFormat_TEXT_FORMAT:
- b, err = prototext.Marshal(msg)
+ b, err = prototext.MarshalOptions{
+ EmitUnknown: req.PrintUnknownFields,
+ }.Marshal(msg)
res = &pb.ConformanceResponse{
Result: &pb.ConformanceResponse_TextPayload{
TextPayload: string(b),
diff --git a/internal/conformance/failing_tests_text_format.txt b/internal/conformance/failing_tests_text_format.txt
index 4ca6f51..f2a30e8 100644
--- a/internal/conformance/failing_tests_text_format.txt
+++ b/internal/conformance/failing_tests_text_format.txt
@@ -1,8 +1,4 @@
-Recommended.Proto3.ProtobufInput.GroupUnknownFields_Drop.TextFormatOutput
-Recommended.Proto3.ProtobufInput.MessageUnknownFields_Drop.TextFormatOutput
Recommended.Proto3.ProtobufInput.MessageUnknownFields_Print.TextFormatOutput
-Recommended.Proto3.ProtobufInput.RepeatedUnknownFields_Drop.TextFormatOutput
-Recommended.Proto3.ProtobufInput.ScalarUnknownFields_Drop.TextFormatOutput
Required.Proto3.TextFormatInput.FloatFieldLargerThanUint64.ProtobufOutput
Required.Proto3.TextFormatInput.FloatFieldLargerThanUint64.TextFormatOutput
Required.Proto3.TextFormatInput.FloatFieldMaxValue.ProtobufOutput
diff --git a/internal/impl/api_export.go b/internal/impl/api_export.go
index 95e7846..6dcf85a 100644
--- a/internal/impl/api_export.go
+++ b/internal/impl/api_export.go
@@ -134,7 +134,10 @@
// MessageStringOf returns the message value as a string,
// which is the message serialized in the protobuf text format.
func (Export) MessageStringOf(m pref.ProtoMessage) string {
- b, _ := prototext.MarshalOptions{AllowPartial: true}.Marshal(m)
+ b, _ := prototext.MarshalOptions{
+ AllowPartial: true,
+ EmitUnknown: true,
+ }.Marshal(m)
return string(b)
}
diff --git a/proto/decode_test.go b/proto/decode_test.go
index c8d34c7..c696778 100644
--- a/proto/decode_test.go
+++ b/proto/decode_test.go
@@ -1748,6 +1748,10 @@
}
func marshalText(m proto.Message) string {
- b, _ := prototext.MarshalOptions{Indent: "\t", AllowPartial: true}.Marshal(m)
+ b, _ := prototext.MarshalOptions{
+ AllowPartial: true,
+ EmitUnknown: true,
+ Indent: "\t",
+ }.Marshal(m)
return string(b)
}