diff --git a/encoding/prototext/encode.go b/encoding/prototext/encode.go index 8856daea..eef63d35 100644 --- a/encoding/prototext/encode.go +++ b/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 } diff --git a/encoding/prototext/encode_test.go b/encoding/prototext/encode_test.go index 839c4f9d..68ceb453 100644 --- a/encoding/prototext/encode_test.go +++ b/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{ diff --git a/internal/conformance/conformance_test.go b/internal/conformance/conformance_test.go index c3196512..1202050a 100644 --- a/internal/conformance/conformance_test.go +++ b/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), diff --git a/internal/conformance/failing_tests_text_format.txt b/internal/conformance/failing_tests_text_format.txt index 4ca6f513..f2a30e80 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 95e7846c..6dcf85a2 100644 --- a/internal/impl/api_export.go +++ b/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) } diff --git a/proto/decode_test.go b/proto/decode_test.go index c8d34c72..c696778c 100644 --- a/proto/decode_test.go +++ b/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) }