CsvHelper 用于从抽象类保存派生类对象

本文关键字:派生 对象 保存 抽象类 用于 CsvHelper | 更新日期: 2023-09-27 18:29:09

[Serializable]
public abstract class AbstractModel : ObservableObject
{
    // nothing.
}
public class RealModel : AbstractModel 
{
    public string PropertyA {get; set;}
    public string PropertyB {get; set;}
}

请注意,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);
}

它抛出错误如下;

没有为类型"抽象模型"映射任何属性

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

我该怎么办?

CsvHelper 用于从抽象类保存派生类对象

我知道

这是三年前的事情,但我试图做同样的事情,我找到了一个黑客来让它工作。

csvHelper 中的违规代码如下:

public virtual Type GetTypeForRecord<T>(T record)
{
    var type = typeof(T);
    if (type == typeof(object))
    {
        type = record.GetType();
    }
    return type;
}

传递给此方法的 C# 泛型是在编译时确定的,因此 T 始终是基类,而不是派生类,但如果在调用 WriteRecord 之前强制转换为类型 object,该方法将使用 GetType() ,这将返回派生类型。

AbstractModel instance = new RealModel()
                             {
                                 PropertyA = "foo",
                                 PropertyA = "bar"
                             };
using (TextWriter file = new StreamWriter("path"))
using (var csv = new CsvWriter(file))
{
    csv.WriteRecord((object)instance);
}

不幸的是,我会说,这就是它应该的方式。输出不是 XML 文件,而是 CSV 文件。CSV文件只有一个标头(如果有的话;并非所有CSV文件都有标头(。

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

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

ID,GivenName,FamilyName
1,John,A
2,Mike,B

标准 CSV 数据示例 2("周年纪念日"(:

Date,Anniversary
1.1.,New year

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

这种方法不好。至少对于 CSV 文件不是。应该始终有一个表,其中包含每行的确切列计数。当数据计数始终准确时,您可以将类转换为通用类,从通用类转换为特定数据类(用于解析器(。

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

1,John,A
1.1.,New year
2,Mike,B

试着问问你自己,解析器应该如何处理这个文件?是的,数据以"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)}";
    }
}

并保存:

using (StreamWriter file = new StreamWriter("path"))
{
  foreach (AbstractModel instance in allInstances)
    file.WriteLine(instance);
}