Просмотр исходного кода

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>
Joe Tsai 6 лет назад
Родитель
Сommit
cd4a31e202

+ 8 - 2
encoding/prototext/encode.go

@@ -38,6 +38,11 @@ type MarshalOptions struct {
 	// 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 @@ func (o MarshalOptions) marshalMessage(m pref.Message) (text.Value, error) {
 	}
 
 	// 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
 }

+ 20 - 0
encoding/prototext/encode_test.go

@@ -820,8 +820,25 @@ req_nested: {}
 			},
 		},
 		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 @@ req_nested: {}
 `,
 	}, {
 		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 @@ req_nested: {}
 `,
 	}, {
 		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 @@ req_nested: {}
 `,
 	}, {
 		desc: "unknown unpack repeated field",
+		mo:   prototext.MarshalOptions{EmitUnknown: true},
 		input: func() proto.Message {
 			m := new(pb2.Scalars)
 			m.ProtoReflect().SetUnknown(pack.Message{

+ 3 - 1
internal/conformance/conformance_test.go

@@ -143,7 +143,9 @@ func handle(req *pb.ConformanceRequest) (res *pb.ConformanceResponse) {
 			},
 		}
 	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),

+ 0 - 4
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

+ 4 - 1
internal/impl/api_export.go

@@ -134,7 +134,10 @@ func (Export) MessageTypeOf(m message) pref.MessageType {
 // 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)
 }
 

+ 5 - 1
proto/decode_test.go

@@ -1748,6 +1748,10 @@ func extend(desc protoreflect.ExtensionType, value interface{}) buildOpt {
 }
 
 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)
 }