У меня есть простая иерархия классов, которую я хочу сериализовать, используя System.Text.Json.
Есть 3 класса. База Shape
. Унаследованные: Box
и Circle
.
Я планирую использовать эти классы в качестве тегового объединения в моем приложении внешнего интерфейса, поэтому я только что ввел свойство дискриминатора Tag
.
Я написал преобразователь типов, который поддерживает сериализацию / десериализацию этой иерархии.
То, что я пытаюсь понять - это лучший подход для реализации такой функциональности или нет. На самом деле результат сериализации довольно уродлив (я добавил комментарий в пример ниже). Я не уверен, что все сделано лучше, так или иначе, это просто работает.
Вот пример того, как я реализовал сериализацию / десериализацию:
using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Serialization.Theory
{
public abstract class Shape
{
public abstract String Tag { get; }
}
public class Box : Shape
{
public override String Tag { get; } = nameof(Box);
public Single Width { get; set; }
public Single Height { get; set; }
public override String ToString()
{
return $"{Tag}: Width={Width}, Height={Height}";
}
}
public class Circle : Shape
{
public override String Tag { get; } = nameof(Circle);
public Single Radius { get; set; }
public override String ToString()
{
return $"{Tag}: Radius={Radius}";
}
}
public class ShapeConverter : JsonConverter<Shape>
{
public override Boolean CanConvert(Type typeToConvert)
{
return typeToConvert == typeof(Circle) || typeToConvert == typeof(Shape);
}
public override Shape Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var raw = reader.GetString();
var doc = JsonDocument.Parse(raw);
var prop = doc.RootElement.EnumerateObject().Where(x => x.Name == "Tag").First();
var value = prop.Value.GetString();
switch (value)
{
case nameof(Circle):
return JsonSerializer.Deserialize<Circle>(raw);
case nameof(Box):
return JsonSerializer.Deserialize<Box>(raw);
default:
throw new NotSupportedException();
}
}
public override void Write(Utf8JsonWriter writer, Shape value, JsonSerializerOptions options)
{
if (value is Circle circle)
{
writer.WriteStringValue(JsonSerializer.SerializeToUtf8Bytes(circle));
}
else if (value is Box box)
{
writer.WriteStringValue(JsonSerializer.SerializeToUtf8Bytes(box));
}
}
}
class Program
{
static void Main(string[] args)
{
// Keep in base class references like it's a property on another object.
Shape origin1 = new Box { Width = 10, Height = 20 };
Shape origin2 = new Circle { Radius = 30 };
var settings = new JsonSerializerOptions();
settings.Converters.Add(new ShapeConverter());
var raw1 = JsonSerializer.Serialize(origin1, settings);
var raw2 = JsonSerializer.Serialize(origin2, settings);
Console.WriteLine(raw1); // "{\u0022Tag\u0022:\u0022Box\u0022,\u0022Width\u0022:10,\u0022Height\u0022:20}"
Console.WriteLine(raw2); // "{\u0022Tag\u0022:\u0022Circle\u0022,\u0022Radius\u0022:30}"
var restored1 = JsonSerializer.Deserialize<Shape>(raw1, settings);
var restored2 = JsonSerializer.Deserialize<Shape>(raw2, settings);
Console.WriteLine(restored1); // Box: Width=10, Height=20
Console.WriteLine(restored2); // Circle: Radius=30
}
}
}