diff --git a/src/CloudNative.CloudEvents.SystemTextJson/JsonEventFormatter.cs b/src/CloudNative.CloudEvents.SystemTextJson/JsonEventFormatter.cs
index 011dd56..6319d0b 100644
--- a/src/CloudNative.CloudEvents.SystemTextJson/JsonEventFormatter.cs
+++ b/src/CloudNative.CloudEvents.SystemTextJson/JsonEventFormatter.cs
@@ -103,6 +103,12 @@ public class JsonEventFormatter : CloudEventFormatter
///
protected JsonDocumentOptions DocumentOptions { get; }
+ ///
+ /// Whether non-UTF8 JSON should be transcoded to UTF-8 before parsing when the target framework supports it.
+ /// Derived classes may override this to exercise the fallback path.
+ ///
+ protected virtual bool UseTranscodingStreamForNonUtf8 => true;
+
///
/// Creates a JsonEventFormatter that uses the default
/// and for serializing and parsing.
@@ -193,15 +199,20 @@ private async Task ReadDocumentAsync(Stream data, ContentType? con
if (encoding is not UTF8Encoding)
{
#if NET5_0_OR_GREATER
- data = Encoding.CreateTranscodingStream(data, encoding, Encoding.UTF8);
-#else
- using var reader = new StreamReader(data, encoding);
- var json = async
- ? await reader.ReadToEndAsync().ConfigureAwait(false)
- : reader.ReadToEnd();
-
- return JsonDocument.Parse(json, DocumentOptions);
+ if (UseTranscodingStreamForNonUtf8)
+ {
+ data = Encoding.CreateTranscodingStream(data, encoding, Encoding.UTF8);
+ }
+ else
#endif
+ {
+ using var reader = new StreamReader(data, encoding);
+ var json = async
+ ? await reader.ReadToEndAsync().ConfigureAwait(false)
+ : reader.ReadToEnd();
+
+ return JsonDocument.Parse(json, DocumentOptions);
+ }
}
return async
diff --git a/test/CloudNative.CloudEvents.UnitTests/SystemTextJson/JsonEventFormatterTest.cs b/test/CloudNative.CloudEvents.UnitTests/SystemTextJson/JsonEventFormatterTest.cs
index 587d6fc..89d35b2 100644
--- a/test/CloudNative.CloudEvents.UnitTests/SystemTextJson/JsonEventFormatterTest.cs
+++ b/test/CloudNative.CloudEvents.UnitTests/SystemTextJson/JsonEventFormatterTest.cs
@@ -538,6 +538,7 @@ public async Task DecodeStructuredModeMessageAsync_Minimal(string charset)
[Theory]
[InlineData("utf-8")]
+ [InlineData("utf-16")]
[InlineData("iso-8859-1")]
public void DecodeStructuredModeMessage_Minimal(string charset)
{
@@ -556,6 +557,31 @@ public void DecodeStructuredModeMessage_Minimal(string charset)
Assert.Equal("test-type", cloudEvent.Type);
Assert.Equal("test-id", cloudEvent.Id);
Assert.Equal(SampleUri, cloudEvent.Source);
+ Assert.Equal(NonAsciiValue, cloudEvent["text"]);
+ }
+
+ [Theory]
+ [InlineData("utf-8")]
+ [InlineData("utf-16")]
+ [InlineData("iso-8859-1")]
+ public async Task DecodeStructuredModeMessage_Minimal_ForcedNonUtf8Fallback(string charset)
+ {
+ var obj = new JObject
+ {
+ ["specversion"] = "1.0",
+ ["type"] = "test-type",
+ ["id"] = "test-id",
+ ["source"] = SampleUriText,
+ ["text"] = NonAsciiValue
+ };
+ var bytes = Encoding.GetEncoding(charset).GetBytes(obj.ToString());
+ var stream = new MemoryStream(bytes);
+ var formatter = new NonTranscodingJsonEventFormatter();
+ var cloudEvent = formatter.DecodeStructuredModeMessage(stream, new ContentType($"application/cloudevents+json; charset={charset}"), null);
+ Assert.Equal("test-type", cloudEvent.Type);
+ Assert.Equal("test-id", cloudEvent.Id);
+ Assert.Equal(SampleUri, cloudEvent.Source);
+ Assert.Equal(NonAsciiValue, cloudEvent["text"]);
}
[Fact]
@@ -1218,6 +1244,11 @@ private static IReadOnlyList DecodeBatchModeMessage(Newtonsoft.Json.
return formatter.DecodeBatchModeMessage(bytes, s_jsonCloudEventBatchContentType, null);
}
+ private sealed class NonTranscodingJsonEventFormatter : JsonEventFormatter
+ {
+ protected override bool UseTranscodingStreamForNonUtf8 => false;
+ }
+
private sealed class YearMonthDayConverter : JsonConverter
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>