反序列化数据的YAML"表"

Bar*_*rry 5 c# yaml yamldotnet

我使用yamldotnet和c#来反序列化由第三方软件应用程序创建的文件.以下YAML文件示例均在应用程序中有效:

#File1
Groups:
  - Name: ATeam
    FirstName, LastName, Age, Height:
      - [Joe, Soap, 21, 184]
      - [Mary, Ryan, 20, 169]
      - [Alex, Dole, 24, 174]

#File2
Groups:
  - Name: ATeam
    FirstName, LastName, Height:
      - [Joe, Soap, 184]
      - [Mary, Ryan, 169]
      - [Alex, Dole, 174]
Run Code Online (Sandbox Code Playgroud)

请注意,File2没有任何Age列,但反序列化器仍然必须识别每行的第三个值是高度而不是年龄.这些数据应该代表一个人的表.以File1为例,Mary Ryan年仅20岁,身高169厘米.反序列化器需要理解它具有的列(对于File2,它只有FirstName,LastName和Height)并相应地将数据存储在正确的对象中:Mary Ryan高169厘米.

类似地,程序文档声明列的顺序并不重要,因此下面的File3是一种同样有效的方式来表示File2中的数据,即使Height现在是第一个:

#File3
Groups:
 - Name: ATeam
   Height, FirstName, LastName:
      - [184, Joe, Soap]
      - [169, Mary, Ryan]
      - [174, Alex, Dole]
Run Code Online (Sandbox Code Playgroud)

我有很多问题:

  1. 这是标准的YAML吗? - 我找不到任何关于在同一行上使用多个键的信息,后面跟一个冒号和值列表来表示数据表.
  2. 我如何使用yamldotnet反序列化?我可以做些修改来帮助吗?
  3. 如果我不能使用yamldotnet,我应该怎么做呢?

Ant*_*bry 2

正如其他答案所述,这是有效的 YAML。但是,文档的结构是特定于应用程序的,并且没有使用 YAML 的任何特殊功能来表达表格。

您可以使用 YamlDotNet 轻松解析此文档。然而你会遇到两个困难。首先,由于列的名称放置在键内,因此您将需要使用一些自定义序列化代码来处理它们。第二个是您需要实现某种抽象才能以表格方式访问数据。

我提出了一个概念证明,它将说明如何解析和读取数据。

首先,创建一个类型来保存 YAML 文档中的信息:

public class Document
{
    public List<Group> Groups { get; set; }
}

public class Group
{
    public string Name { get; set; }

    public IEnumerable<string> ColumnNames { get; set; }

    public IList<IList<object>> Rows { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后实现IYamlTypeConverter解析Group类型:

public class GroupYamlConverter : IYamlTypeConverter
{
    private readonly Deserializer deserializer;

    public GroupYamlConverter(Deserializer deserializer)
    {
        this.deserializer = deserializer;
    }

    public bool Accepts(Type type)
    {
        return type == typeof(Group);
    }

    public object ReadYaml(IParser parser, Type type)
    {
        var group = new Group();

        var reader = new EventReader(parser);
        do
        {
            var key = reader.Expect<Scalar>();
            if(key.Value == "Name")
            {
                group.Name = reader.Expect<Scalar>().Value;
            }
            else
            {
                group.ColumnNames = key.Value
                    .Split(',')
                    .Select(n => n.Trim())
                    .ToArray();

                group.Rows = deserializer.Deserialize<IList<IList<object>>>(reader);
            }
        } while(!reader.Accept<MappingEnd>());
        reader.Expect<MappingEnd>();

        return group;
    }

    public void WriteYaml(IEmitter emitter, object value, Type type)
    {
        throw new NotImplementedException("TODO");
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,将转换器注册到反序列化器中并反序列化文档:

var deserializer = new Deserializer();
deserializer.RegisterTypeConverter(new GroupYamlConverter(deserializer));

var document = deserializer.Deserialize<Document>(new StringReader(yaml));
Run Code Online (Sandbox Code Playgroud)

您可以在此处测试完整的工作示例

这只是一个概念证明,但它应该作为您自己实施的指南。可以改进的地方包括:

  • 检查并处理无效文件。
  • 提高Group班级水平。也许使它不可变,并添加一个索引器。
  • WriteYaml如果需要序列化支持,则实现该方法。