我遇到了一个序列化多态数据的问题。
比如说我有以下的类。
public class ItemBase
{
public ItemBase(string itemType)
{
ItemType = itemType;
}
public string ItemType { get; private set; }
public List<ItemBase> Children { get; private set; } = new List<ItemBase>();
}
public class RootItem : ItemBase
{
public RootItem() : base(Models.ItemType.RootItem.Type)
{
}
}
public class Text : ItemBase
{
public Text(string content) : base(Models.ItemType.Text.Type)
{
Content = content;
}
public string Content { get; private set; }
}
在一个应用阶段,我有 RootItem
的所有子代。现在,我需要从ASP.NET Core 3.0 API中返回该值,如何才能干净利落地完成这个任务?
我尝试了一个自定义的Json转换器,多态的情况下一切都没问题,但是子代的问题是,数据不是像父代子代那样输出。类似这样的问题。
private void Serialize(Utf8JsonWriter writer, ItemBase value)
{
if (value is Paragraph paragraph)
JsonSerializer.Serialize(writer, paragraph);
if (value is Text text)
JsonSerializer.Serialize(writer, text);
....
}
最后我用了一个自定义的JSON转换器。我希望它能帮助别人。因为父子关系,因为多态对象,我用递归的方式实现了自定义JSON读写。最终结束了这样的事情。
public override RootItem Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var item = _toDomainParser.ParseFromJson(ref reader);
return item;
}
public override void Write(Utf8JsonWriter writer, RootItem value, JsonSerializerOptions options)
{
_toJsonParser.ParseFromRootItem(value, writer);
}
This for read purposes.
public ItemBase ParseFromReader(ref Utf8JsonReader reader, ItemBase parent)
{
//in case of start json we will already have start, but in case of deeper items we don't, so read it
if (reader.TokenType != JsonTokenType.StartObject)
reader.Read();//TODO delete if it is not use|also in case for deep in array
var (propName, value) = ReadString(ref reader);
if (string.IsNullOrEmpty(propName) && string.IsNullOrEmpty(value))
return parent;
var item = DefineItem(value, ref reader);
if (item == null)
throw new Exception("Cannot parse item from json");
parent?.Children.Add(item);
reader.Read(); // read next json object to check whether there are children
if (reader.TokenType == JsonTokenType.PropertyName)
{
//handle there children, because the last property are children
reader.Read(); //this should be start of the array
ParseFromReader(ref reader, item);
reader.Read(); //this should be end of the array
}
if (reader.TokenType == JsonTokenType.EndObject) // in case this is sibling
{
reader.Read();
if (reader.TokenType == JsonTokenType.EndArray)
return item;
ParseFromReader(ref reader, parent);
}
return item;
}
private Img GetImg(ref Utf8JsonReader reader)
{
var (prop, val) = ReadString(ref reader);
return new Img(val);
}
private (string propertyName, string value) ReadString(ref Utf8JsonReader reader)
{
var isRead = reader.Read();//read propertyName
if (!isRead)
return (null, null);
var propertyName = reader.GetString();
isRead = reader.Read();//read property itself
if (!isRead)
return (null, null);
var value = reader.GetString();
return (propertyName, value);
}
这个用于写。
private void ParseItem(ItemBase item, Utf8JsonWriter writer)
{
writer.WriteStartObject();
if (item is Paragraph paragraph)
WriteSingleObject(paragraph, writer);
if (item is Text text)
WriteSingleObject(text, writer);
if (item is RootItem rootItem)
WriteSingleObject(rootItem, writer);
if (item is NewLine newLine)
WriteSingleObject(newLine, writer);
if (item is Link link)
WriteSingleObject(link, writer);
if (item is Strong strong)
WriteSingleObject(strong, writer);
if (item is NumberList numberList)
WriteSingleObject(numberList, writer);
if (item is ListItem listItem)
WriteSingleObject(listItem, writer);
if (item is Pre pre)
WriteSingleObject(pre, writer);
if (item is Code code)
WriteSingleObject(code, writer);
if (item is Figure figure)
WriteSingleObject(figure, writer);
if (item is Img img)
WriteSingleObject(img, writer);
if (item.Children != null && item.Children.Count > 0)
{
writer.WriteStartArray(ItemBase.ChildrenNaming);
foreach (var child in item.Children)
ParseItem(child, writer);
writer.WriteEndArray();
}
writer.WriteEndObject();