using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
using Xunit;
namespace System.Text.Json.Serialization.Tests
Assert.Null(obj.MyOverflow);
}
+ private class ClassWithExtensionData<T>
+ {
+ [JsonExtensionData]
+ public T Overflow { get; set; }
+ }
+
+ public class CustomOverflowDictionary<T> : Dictionary<string, T>
+ {
+ }
+
+ public class DictionaryOverflowConverter : JsonConverter<Dictionary<string, object>>
+ {
+ public override Dictionary<string, object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, Dictionary<string, object> value, JsonSerializerOptions options)
+ {
+ writer.WriteString("MyCustomOverflowWrite", "OverflowValueWrite");
+ }
+ }
+
+ public class JsonElementOverflowConverter : JsonConverter<Dictionary<string, JsonElement>>
+ {
+ public override Dictionary<string, JsonElement> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, Dictionary<string, JsonElement> value, JsonSerializerOptions options)
+ {
+ writer.WriteString("MyCustomOverflowWrite", "OverflowValueWrite");
+ }
+ }
+
+ public class CustomObjectDictionaryOverflowConverter : JsonConverter<CustomOverflowDictionary<object>>
+ {
+ public override CustomOverflowDictionary<object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, CustomOverflowDictionary<object> value, JsonSerializerOptions options)
+ {
+ writer.WriteString("MyCustomOverflowWrite", "OverflowValueWrite");
+ }
+ }
+
+ public class CustomJsonElementDictionaryOverflowConverter : JsonConverter<CustomOverflowDictionary<JsonElement>>
+ {
+ public override CustomOverflowDictionary<JsonElement> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, CustomOverflowDictionary<JsonElement> value, JsonSerializerOptions options)
+ {
+ writer.WriteString("MyCustomOverflowWrite", "OverflowValueWrite");
+ }
+ }
+
+ [Theory]
+ [InlineData(typeof(Dictionary<string, object>), typeof(DictionaryOverflowConverter))]
+ [InlineData(typeof(Dictionary<string, JsonElement>), typeof(JsonElementOverflowConverter))]
+ [InlineData(typeof(CustomOverflowDictionary<object>), typeof(CustomObjectDictionaryOverflowConverter))]
+ [InlineData(typeof(CustomOverflowDictionary<JsonElement>), typeof(CustomJsonElementDictionaryOverflowConverter))]
+ public static void ExtensionProperty_SupportsWritingToCustomSerializerWithOptions(Type overflowType, Type converterType)
+ {
+ typeof(ExtensionDataTests)
+ .GetMethod(nameof(ExtensionProperty_SupportsWritingToCustomSerializerWithOptionsInternal), BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(overflowType, converterType)
+ .Invoke(null, null);
+ }
+
+ private static void ExtensionProperty_SupportsWritingToCustomSerializerWithOptionsInternal<TDictionary, TConverter>()
+ where TDictionary : new()
+ where TConverter : JsonConverter, new()
+ {
+ var root = new ClassWithExtensionData<TDictionary>()
+ {
+ Overflow = new TDictionary()
+ };
+
+ var options = new JsonSerializerOptions();
+ options.Converters.Add(new TConverter());
+
+ string json = JsonSerializer.Serialize(root, options);
+ Assert.Equal(@"{""MyCustomOverflowWrite"":""OverflowValueWrite""}", json);
+ }
+
+ private interface IClassWithOverflow<T>
+ {
+ public T Overflow { get; set; }
+ }
+
+ private class ClassWithExtensionDataWithAttributedConverter : IClassWithOverflow<Dictionary<string, object>>
+ {
+ [JsonExtensionData]
+ [JsonConverter(typeof(DictionaryOverflowConverter))]
+ public Dictionary<string, object> Overflow { get; set; }
+ }
+
+ private class ClassWithJsonElementExtensionDataWithAttributedConverter : IClassWithOverflow<Dictionary<string, JsonElement>>
+ {
+ [JsonExtensionData]
+ [JsonConverter(typeof(JsonElementOverflowConverter))]
+ public Dictionary<string, JsonElement> Overflow { get; set; }
+ }
+
+ private class ClassWithCustomElementExtensionDataWithAttributedConverter : IClassWithOverflow<CustomOverflowDictionary<object>>
+ {
+ [JsonExtensionData]
+ [JsonConverter(typeof(CustomObjectDictionaryOverflowConverter))]
+ public CustomOverflowDictionary<object> Overflow { get; set; }
+ }
+
+ private class ClassWithCustomJsonElementExtensionDataWithAttributedConverter : IClassWithOverflow<CustomOverflowDictionary<JsonElement>>
+ {
+ [JsonExtensionData]
+ [JsonConverter(typeof(CustomJsonElementDictionaryOverflowConverter))]
+ public CustomOverflowDictionary<JsonElement> Overflow { get; set; }
+ }
+
+ [Theory]
+ [InlineData(typeof(ClassWithExtensionDataWithAttributedConverter), typeof(Dictionary<string, object>))]
+ [InlineData(typeof(ClassWithJsonElementExtensionDataWithAttributedConverter), typeof(Dictionary<string, JsonElement>))]
+ [InlineData(typeof(ClassWithCustomElementExtensionDataWithAttributedConverter), typeof(CustomOverflowDictionary<object>))]
+ [InlineData(typeof(ClassWithCustomJsonElementExtensionDataWithAttributedConverter), typeof(CustomOverflowDictionary<JsonElement>))]
+ public static void ExtensionProperty_SupportsWritingToCustomSerializerWithExplicitConverter(Type attributedType, Type dictionaryType)
+ {
+ typeof(ExtensionDataTests)
+ .GetMethod(nameof(ExtensionProperty_SupportsWritingToCustomSerializerWithExplicitConverterInternal), BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(attributedType, dictionaryType)
+ .Invoke(null, null);
+ }
+
+ private static void ExtensionProperty_SupportsWritingToCustomSerializerWithExplicitConverterInternal<TRoot, TDictionary>()
+ where TRoot : IClassWithOverflow<TDictionary>, new()
+ where TDictionary : new()
+ {
+ var root = new TRoot()
+ {
+ Overflow = new TDictionary()
+ };
+
+ string json = JsonSerializer.Serialize(root);
+ Assert.Equal(@"{""MyCustomOverflowWrite"":""OverflowValueWrite""}", json);
+ }
+
+ [Theory]
+ [InlineData(typeof(Dictionary<string, object>), typeof(DictionaryOverflowConverter), typeof(object))]
+ [InlineData(typeof(Dictionary<string, JsonElement>), typeof(JsonElementOverflowConverter), typeof(JsonElement))]
+ [InlineData(typeof(CustomOverflowDictionary<object>), typeof(CustomObjectDictionaryOverflowConverter), typeof(object))]
+ [InlineData(typeof(CustomOverflowDictionary<JsonElement>), typeof(CustomJsonElementDictionaryOverflowConverter), typeof(JsonElement))]
+ public static void ExtensionProperty_IgnoresCustomSerializerWithOptions(Type overflowType, Type converterType, Type elementType)
+ {
+ typeof(ExtensionDataTests)
+ .GetMethod(nameof(ExtensionProperty_IgnoresCustomSerializerWithOptionsInternal), BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(overflowType, elementType, converterType)
+ .Invoke(null, null);
+ }
+
+ private static void ExtensionProperty_IgnoresCustomSerializerWithOptionsInternal<TDictionary, TOverflowItem, TConverter>()
+ where TConverter : JsonConverter, new()
+ where TDictionary : IDictionary<string, TOverflowItem>
+ {
+ var options = new JsonSerializerOptions();
+ options.Converters.Add(new TConverter());
+
+ ClassWithExtensionData<TDictionary> obj
+ = JsonSerializer.Deserialize<ClassWithExtensionData<TDictionary>>(@"{""TestKey"":""TestValue""}", options);
+
+ Assert.Equal("TestValue", ((JsonElement)(object)obj.Overflow["TestKey"]).GetString());
+ }
+
+ [Theory]
+ [InlineData(typeof(ClassWithExtensionDataWithAttributedConverter), typeof(Dictionary<string, object>), typeof(object))]
+ [InlineData(typeof(ClassWithJsonElementExtensionDataWithAttributedConverter), typeof(Dictionary<string, JsonElement>), typeof(JsonElement))]
+ [InlineData(typeof(ClassWithCustomElementExtensionDataWithAttributedConverter), typeof(CustomOverflowDictionary<object>), typeof(object))]
+ [InlineData(typeof(ClassWithCustomJsonElementExtensionDataWithAttributedConverter), typeof(CustomOverflowDictionary<JsonElement>), typeof(JsonElement))]
+ public static void ExtensionProperty_IgnoresCustomSerializerWithExplicitConverter(Type attributedType, Type dictionaryType, Type elementType)
+ {
+ typeof(ExtensionDataTests)
+ .GetMethod(nameof(ExtensionProperty_IgnoresCustomSerializerWithExplicitConverterInternal), BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(attributedType, dictionaryType, elementType)
+ .Invoke(null, null);
+ }
+
+ private static void ExtensionProperty_IgnoresCustomSerializerWithExplicitConverterInternal<TRoot, TDictionary, TOverflowItem>()
+ where TRoot : IClassWithOverflow<TDictionary>, new()
+ where TDictionary : IDictionary<string, TOverflowItem>
+ {
+ ClassWithExtensionData<TDictionary> obj
+ = JsonSerializer.Deserialize<ClassWithExtensionData<TDictionary>>(@"{""TestKey"":""TestValue""}");
+
+ Assert.Equal("TestValue", ((JsonElement)(object)obj.Overflow["TestKey"]).GetString());
+ }
+
[Fact]
public static void ExtensionPropertyObjectValue_Empty()
{