10

使用YamlDotNet,我试图反序列化以下 YAML:

Collection:
  - Type: TypeA
    TypeAProperty: value1
  - Type: TypeB
    TypeBProperty: value2

Type属性是 下所有对象的必需属性Collection。其余属性取决于类型。

这是我理想的对象模型:

public class Document
{
  public IEnumerable<IBaseObject> Collection { get; set; }
}

public interface IBaseObject
{
  public string Type { get; }
}

public class TypeAClass : IBaseObject
{
  public string Type { get; set; }
  public string TypeAProperty { get; set; }
}

public class TypeBClass : IBaseObject
{
  public string Type { get; set; }
  public string TypeBProperty { get; set; }
}

根据我的阅读,我认为我最好的选择是使用自定义节点反序列化器,从INodeDeserializer. 作为概念证明,我可以这样做:

public class MyDeserializer : INodeDeserializer
{
  public bool Deserialize(IParser parser, Type expectedType, Func<IParser, Type, object> nestedObjectDeserializer, out object value)
  {
    if (expectedType == typeof(IBaseObject))
    {
      Type type = typeof(TypeAClass);
      value = nestedObjectDeserializer(parser, type);
      return true;
    }

    value = null;
    return false;
  }
}

我现在的问题是如何Type在调用之前动态确定要选择的nestedObjectDeserializer.

使用 JSON.Net 时,我能够使用 a CustomCreationConverter,将子 JSON 读入 a JObject,确定我的类型,然后从 中创建一个新JsonReaderJObject并重新解析对象。

有没有办法我可以阅读,回滚,然后重新阅读nestedObjectDeserializer

是否有另一种对象类型我可以调用nestedObjectDeserializer,然后从中读取Type属性,最后通过派生类型的正常 YamlDotNet 解析进行?

4

1 回答 1

1

这并不容易。这是一个 GitHub问题,解释了如何使用 YamlDotNet 进行多态序列化。

在您的情况下,一个简单的解决方案是进行两步反序列化。首先,您反序列化为某种中间形式,然后将其转换为您的模型。这相对容易,因为您限制了对 YamlDotNet 内部的挖掘:

public class Step1Document
{
    public List<Step1Element> Collection { get; set; }

    public Document Upcast()
    {
        return new Document
        {
            Collection = Collection.Select(m => m.Upcast()).ToList()
        };
    }
}

public class Step1Element
{
    // Fields from TypeA and TypeB
    public string Type { get; set; }
    public string TypeAProperty { get; set; }
    public string TypeBProperty { get; set; }

    internal IBaseObject Upcast()
    {
        if(Type == "TypeA")
        {
            return new TypeAClass
            {
                Type = Type,
                TypeAProperty = TypeAProperty
            };
        }
        if (Type == "TypeB")
        {
            return new TypeBClass
            {
                Type = Type,
                TypeBProperty = TypeBProperty
            };
        }

        throw new NotImplementedException(Type);
    }
}

而要反序列化:

var serializer = new DeserializerBuilder().Build();

var document = serializer.Deserialize<Step1Document>(data).Upcast();
于 2020-01-03T13:46:59.193 回答