Skip to content

Instantly share code, notes, and snippets.

@NN---
Created September 11, 2023 08:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NN---/7863622be9b488b973a01249427cb31f to your computer and use it in GitHub Desktop.
Save NN---/7863622be9b488b973a01249427cb31f to your computer and use it in GitHub Desktop.
Newtonsoft.Json can serialize this successfully while System.Text.Json doesn't do it:
```cs
var la = new List<A> { new B() { I = 1, J = 2 } };
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(la));
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(la));
class A
{
public int I { get; set; }
}
class B : A
{
public int J { get; set; }
}
```
```
[{"J":2,"I":1}]
[{"I":1}]
```
There is a page describing how to work with polymorphic objects: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0 .
However, it required writing down all possible types.
Newtonsoft.Json writes an object simply as dictionary thus making possible to write any type regardless the hierarachy.
I suggest adding an option either by options or by attribute allowing serializing polymorphic type the same way Newtonsoft.Json does.
```cs
var la = new List<A> { new B() { I = 1, J = 2 } };
// 1. using options
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(la));
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(la,
new JsonSerializerOptions {
SerializePolymorphicTypes = true // Serialize polymorphic types
SerializeAsObject = true // Alternative way by treating types as key-value objects which has the same result
}));
// 2. using attribute
[JsonSerializePolymorphic] // Serialize derived types
class A
{
public int I { get; set; }
}
class B : A
{
public int J { get; set; }
}
```
```
[{"J":2,"I":1}]
[{"J":2,"I":1}]
```
Another option is to use TypeInfoResolver for detecting derived type:
```cs
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(la,
new JsonSerializerOptions {
TypeInfoResolver = new PolymorphicTypeResolver()
}));
public class PolymorphicTypeResolver : DefaultJsonTypeInfoResolver
{
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
{
JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
if (type.GetCustomAttribute<JsonPolymorphicAttribute>() is { } disc)
{
jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
{
TypeDiscriminatorPropertyName = disc.TypeDiscriminatorPropertyName,
IgnoreUnrecognizedTypeDiscriminators = true,
UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
};
foreach (var d in Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsSubclassOf(type)))
{
jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType( d));
}
}
return jsonTypeInfo;
}
}
```
Alternatively everything can be defined as `object` which is not so nice making your code dynamically typed.
```cs
var la = new List<object> { new B() { I = 1, J = 2 } };
```
Or serializing every object manually as key-value:
```cs
var sla = System.Text.Json.JsonSerializer.Serialize(la, new JsonSerializerOptions
{
Converters = { new ObjectConverterFactory() },
});
Console.WriteLine(sla);
public class ObjectConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
return !typeToConvert.IsPrimitive &&
!typeToConvert.IsEnum &&
typeToConvert.Assembly != typeof(object).Assembly;
}
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return new ObjectConverter(options);
}
}
public class ObjectConverter : JsonConverter<object>
{
private readonly JsonSerializerOptions _options;
public ObjectConverter(JsonSerializerOptions options)
{
_options = options;
}
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
Type type = value.GetType();
writer.WriteStartObject();
foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (pi.GetValue(value) is { } nonNullValue)
{
writer.WritePropertyName(pi.Name);
writer.WriteRawValue(STJ.JsonSerializer.Serialize(nonNullValue, _options));
}
}
writer.WriteEndObject();
}
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment