diff --git a/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs b/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs index 3564135..1a59ec2 100644 --- a/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs +++ b/src/CloudNative.CloudEvents.NewtonsoftJson/JsonEventFormatter.cs @@ -80,6 +80,8 @@ namespace CloudNative.CloudEvents.NewtonsoftJson /// public class JsonEventFormatter : CloudEventFormatter { + private static readonly Encoding noBomUtf8 = new UTF8Encoding(false); + private static readonly IReadOnlyDictionary expectedTokenTypesForReservedAttributes = new Dictionary { @@ -395,7 +397,7 @@ public override ReadOnlyMemory EncodeStructuredModeMessage(CloudEvent clou CharSet = Encoding.UTF8.WebName }; - var stream = new MemoryStream(); + var stream = this.GetMemoryStream(); var writer = CreateJsonTextWriter(stream); WriteCloudEventForBatchOrStructuredMode(writer, cloudEvent); writer.Flush(); @@ -425,7 +427,7 @@ public override ReadOnlyMemory EncodeBatchModeMessage(IEnumerable EncodeBinaryModeEventData(CloudEvent cloudE ContentType contentType = new ContentType(cloudEvent.DataContentType ?? JsonMediaType); if (IsJsonMediaType(contentType.MediaType)) { - // TODO: Make this more efficient. We could write to a StreamWriter with a MemoryStream, - // but then we end up with a BOM in most cases, which I suspect we don't want. - // An alternative is to make sure that contentType.GetEncoding() always returns an encoding - // without a preamble (or rewrite StreamWriter...) - var stringWriter = new StringWriter(); - Serializer.Serialize(stringWriter, cloudEvent.Data); - return MimeUtilities.GetEncoding(contentType).GetBytes(stringWriter.ToString()); + var encoding = MimeUtilities.GetEncoding(contentType); + if (ReferenceEquals(encoding, Encoding.UTF8)) + { + using var stream = this.GetMemoryStream(); + using var writer = new StreamWriter(stream, noBomUtf8); + Serializer.Serialize(writer, cloudEvent.Data); + writer.Flush(); + return stream.ToArray(); + } + else + { + var stringWriter = new StringWriter(); + Serializer.Serialize(stringWriter, cloudEvent.Data); + return encoding.GetBytes(stringWriter.ToString()); + } } if (contentType.MediaType.StartsWith("text/") && cloudEvent.Data is string text) { @@ -614,6 +624,13 @@ public override void DecodeBinaryModeEventData(ReadOnlyMemory body, CloudE } } + /// + /// Creates a stream that can be written to to provide buffered array data. + /// + /// A memory stream. + protected virtual MemoryStream GetMemoryStream() + => new MemoryStream(); + /// /// Creates a for the given stream. This may be overridden in derived classes to /// customize the JSON parsing process, subject to the constraints listed in the remarks.