简体   繁体   English

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

[英]CsvHelper to save derived class object from abstract class

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

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

Note that ObservableObject is from Mvvm-light. 请注意, ObservableObject来自Mvvm-light。

With above models, I used CsvHelper as below. 对于上面的模型,我使用CsvHelper如下。

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

using (TextWriter file = new StreamWriter("path"))
using (var csv = new CsvWriter(file))
{
    csv.WriteRecord(instance);
}

It throws error as below; 它抛出错误如下;

No properties are mapped for type 'AbstractModel' 没有为“AbstractModel”类型映射属性

It works fine when I set RealModel instance = new RealModel(); 当我设置RealModel instance = new RealModel();时它工作正常RealModel instance = new RealModel(); instead. 代替。 But, I have various derived classes and want to save them in a single Save method. 但是,我有各种派生类,并希望将它们保存在一个Save方法中。

How can I do? 我能怎么做?

Unfortunately I would say, this is the way it is supposed to be. 不幸的是我会说,这就是应该的方式。 The output is not an XML file, but a CSV file. 输出不是XML文件,而是CSV文件。 And CSV file has only a single header (when it has any; not all CSV files have a header). 并且CSV文件只有一个标题(当它有任何标题时;并非所有CSV文件都有标题)。

The parser can not determine class by data in line. 解析器无法按行数据确定类。

Standard CSV data example 1 (names): 标准CSV数据示例1(名称):

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

Standard CSV data example 2 ("anniversaries"): 标准CSV数据示例2(“纪念日”):

Date,Anniversary
1.1.,New year

When you save to CSV file, you can not have two "header lines"; 保存为CSV文件时,不能有两个“标题行”; how should the parser know, to which child class decode the output? 解析器应该如何知道哪个子类解码输出? Names or anniversaries? 姓名或纪念日? Why is the first line "name" and the second "anniversary"? 为什么第一行是“名称”,第二行是“周年纪念日”? Why is it not another way around? 为什么不是另一种方式?

This approach is not good. 这种方法并不好。 At least not for a CSV file. 至少不适用于CSV文件。 There should be always a table with exact count of column for each line. 应始终有一个表格,每行都有精确的列数。 When the count of data is always exact, you can me converters from your class to a universal class and from universal class to specific data classes (for parser). 当数据的计数总是精确的时,您可以将类转换器从类转换为通用类,从通用类转换为特定数据类(用于解析器)。

Non standard data example: (names and anniversaries; no header line): 非标准数据示例:(名称和纪念日;没有标题行):

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

Try to ask your self, how should the parser handle this file? 试着问自己,解析器应该如何处理这个文件? Yes the data are stored in "the CSV way", but I don't think, these data are correct. 是的,数据以“CSV方式”存储,但我不认为,这些数据是正确的。 This is not the way it should be. 这不是它应该的方式。

I know there are situations, when you need to use this approach (formatted data; first item specify type, all other values in line are data for given type). 我知道有些情况,当你需要使用这种方法时(格式化数据;第一项指定类型,行中的所有其他值都是给定类型的数据)。 But for this usage you need to make a different approach to store and parse the data. 但是对于这种用法,您需要采用不同的方法来存储和解析数据。

A example of different approach: 不同方法的一个例子:

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)}";
    }
}

And save with: 并保存:

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

I know this is three years old, but I was attempting to do the same thing, and I've found a hack to get it to work. 我知道这已经有三年了,但是我试图做同样的事情,而且我找到了一个让它发挥作用的黑客。

The offending code in csvHelper is the following: csvHelper中的违规代码如下:

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

    return type;
}

The C# generic that is passed down to this method is determined at compile time, so T is always going to be the base class, not the derived class, but if you cast to to type object before calling WriteRecord , the method will use GetType() , which will return the derived type. 传递给此方法的C#泛型在编译时确定,因此T始终是基类,而不是派生类,但如果在调用WriteRecord之前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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM