如何使用YamlDotNet将YAML解析为派生集合?

时间:2017-03-21 22:00:21

标签: c# parsing yamldotnet

使用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时,我能够使用CustomCreationConverter,将子JSON读入JObject,确定我的类型,然后从{创建一个新的JsonReader {1}}并重新解析对象。

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

我可以在nestedObjectDeserializer上调用另一个对象类型,然后从那里读取nestedObjectDeserializer属性,最后通过正常的YamlDotNet解析派生类型吗?

1 个答案:

答案 0 :(得分:0)

这不容易。这是一个GitHub issue,说明了如何使用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();