diff --git a/src/Google.Events.Protobuf.Tests/ProtobufJsonCloudEventFormatterTest.cs b/src/Google.Events.Protobuf.Tests/ProtobufJsonCloudEventFormatterTest.cs index 7daf26a..e5e2c21 100644 --- a/src/Google.Events.Protobuf.Tests/ProtobufJsonCloudEventFormatterTest.cs +++ b/src/Google.Events.Protobuf.Tests/ProtobufJsonCloudEventFormatterTest.cs @@ -14,6 +14,7 @@ using CloudNative.CloudEvents; using Google.Events.Protobuf.Cloud.Storage.V1; +using Google.Protobuf; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -84,6 +85,21 @@ public void DecodeBinary_NoData() Assert.Null(cloudEvent.Data); } + [Fact] + public void DecodeBinary_ProtobufWireFormat() + { + var cloudEvent = CreateSampleEvent(); + var expectedData = cloudEvent.Data; + var binaryData = ((IMessage) expectedData).ToByteArray(); + cloudEvent.Data = null; + cloudEvent.DataContentType = "application/protobuf"; + + var converter = new ProtobufJsonCloudEventFormatter(); + converter.DecodeBinaryModeEventData(binaryData, cloudEvent); + var actualData = cloudEvent.Data; + Assert.Equal(expectedData, actualData); + } + [Fact] public void DecodeBatch() { diff --git a/src/Google.Events.Protobuf/ProtobufJsonCloudEventFormatter.cs b/src/Google.Events.Protobuf/ProtobufJsonCloudEventFormatter.cs index 6d2a8a7..0a24a8e 100644 --- a/src/Google.Events.Protobuf/ProtobufJsonCloudEventFormatter.cs +++ b/src/Google.Events.Protobuf/ProtobufJsonCloudEventFormatter.cs @@ -28,10 +28,14 @@ namespace Google.Events.Protobuf /// /// which is able to serialize protobuf messages in JSON. /// (Note that this does not implement the protobuf CloudEvent format, which is separate.) + /// Additionally, when deserializing binary mode events, if the data content type has a media type of "application/protobuf", + /// this will deserialize the body as protobuf binary data. /// /// The type of message to serialize/deserialize public class ProtobufJsonCloudEventFormatter : CloudEventFormatter where T : class, IMessage, new() { + private const string BinaryProtobufMediaType = "application/protobuf"; + private static readonly JsonParser s_jsonParser = new JsonParser(JsonParser.Settings.Default.WithIgnoreUnknownFields(true)); // Note: although we delegate to a JsonEventFormatter (via StructuredEventFormatter) and *could* just derive from JsonEventFormatter, @@ -39,8 +43,25 @@ namespace Google.Events.Protobuf private static readonly CloudEventFormatter s_structuredEventFormatter = new StructuredEventFormatter(); /// - public override void DecodeBinaryModeEventData(ReadOnlyMemory body, CloudEvent cloudEvent) => - cloudEvent.Data = body.Length == 0 ? null : s_jsonParser.Parse(new StreamReader(BinaryDataUtilities.AsStream(body))); + public override void DecodeBinaryModeEventData(ReadOnlyMemory body, CloudEvent cloudEvent) + { + if (body.Length == 0) + { + cloudEvent.Data = null; + return; + } + ContentType dataContentType = new ContentType(cloudEvent.DataContentType ?? "application/json"); + cloudEvent.Data = + dataContentType.MediaType == BinaryProtobufMediaType ? ParseBinaryProtobuf(body) + : s_jsonParser.Parse(new StreamReader(BinaryDataUtilities.AsStream(body))); + } + + private T ParseBinaryProtobuf(ReadOnlyMemory body) + { + var message = new T(); + message.MergeFrom(body.Span); + return message; + } // TODO: Handle other structured modes beyond JSON?