CsvHelper从抽象类中保存派生类对象

You*_*jae 5 c# csvhelper

[Serializable]
public abstract class AbstractModel : ObservableObject
{
    // nothing.
}

public class RealModel : AbstractModel 
{
    public string PropertyA {get; set;}
    public string PropertyB {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

请注意,这ObservableObject是来自Mvvm-light.

对于上面的模型,我使用CsvHelper如下.

AbstractModel instance = new RealModel()
                             {
                                 PropertyA = "foo",
                                 PropertyA = "bar"
                             };

using (TextWriter file = new StreamWriter("path"))
using (var csv = new CsvWriter(file))
{
    csv.WriteRecord(instance);
}
Run Code Online (Sandbox Code Playgroud)

它抛出错误如下;

没有为"AbstractModel"类型映射属性

当我设置时,它工作正常RealModel instance = new RealModel();.但是,我有各种派生类,并希望将它们保存在一个Save方法中.

我能怎么做?

Jul*_*ulo 0

不幸的是,我想说,事情本来就应该如此。输出不是 XML 文件,而是 CSV 文件。并且 CSV 文件只有一个标头(如果有的话;并非所有 CSV 文件都有标头)。

解析器无法通过行中的数据确定类。

标准 CSV 数据示例 1(名称):

ID,GivenName,FamilyName
1,John,A
2,Mike,B
Run Code Online (Sandbox Code Playgroud)

标准 CSV 数据示例 2(“周年纪念日”):

Date,Anniversary
1.1.,New year
Run Code Online (Sandbox Code Playgroud)

当您保存到 CSV 文件时,不能有两个“标题行”;解析器应该如何知道哪个子类将输出解码?名字还是纪念日?为什么第一行是“名字”,第二行是“周年纪念”?为什么不换个方式呢?

这种做法并不好。至少对于 CSV 文件来说不是。应该始终有一个表格,其中每行都有准确的列数。当数据计数始终准确时,您可以使用从您的类到通用类以及从通用类到特定数据类(用于解析器)的转换器。

非标准数据示例:(姓名和周年纪念日;无标题行):

1,John,A
1.1.,New year
2,Mike,B
Run Code Online (Sandbox Code Playgroud)

试着问问自己,解析器应该如何处理这个文件?是的,数据以“CSV 方式”存储,但我不认为这些数据是正确的。这不应该是这样的。

我知道在某些情况下,您需要使用这种方法(格式化数据;第一项指定类型,行中的所有其他值都是给定类型的数据)。但对于这种用法,您需要采用不同的方法来存储和解析数据。

不同方法的示例:

public abstract class AbstractModel : ObservableObject
{
    // no property

    protected string Prefix(object data)
    {
       if (ReferenceEquals(data, null))
         return string.Empty;
       string result = data.ToString();
       if (data.Contains(",") || data.Contains("\""))
         return $"\"{data}\"";
       return data;
    }

}

public class RealModel : AbstractModel 
{
    public string PropertyA {get; set;}
    public string PropertyB {get; set;}

    public override string ToString()
    {
       return $"{Prefix(PropertyA)},{Prefix(PropertyB)}";
    }
}
Run Code Online (Sandbox Code Playgroud)

并保存:

using (StreamWriter file = new StreamWriter("path"))
{
  foreach (AbstractModel instance in allInstances)
    file.WriteLine(instance);
}
Run Code Online (Sandbox Code Playgroud)