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

时间:2016-01-05 10:21:14

标签: c# csvhelper

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

如下所示抛出错误;

  

没有为“AbstractModel”

类型映射属性

我设置RealModel instance = new RealModel();时效果很好。但是,我有各种派生类,并希望将它们保存在一个Save方法中。

我该怎么办?

2 个答案:

答案 0 :(得分:0)

不幸的是我会说,这就是应该的样子。 输出不是XML文件,而是CSV文件。并且CSV文件只有一个标题(当它有任何标题时;并非所有CSV文件都有标题)。

解析器无法按行数据确定类。

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

namespace Application
{
    [Activity(Label = "WebPage", Theme = "@android:style/Theme.NoTitleBar")]
    public class WebPage : Activity
    {
        public bool isSchedule;

        private WebView _webView;
        private string _url;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            SetContentView(Resource.Layout.WebPage);

            _webView = FindViewById<WebView>(Resource.Id.webView);

            new DisplayWebPage(_webView, GetUrl());
        }

        public string GetUrl()
        {
            return _url;
        }

        public void SetUrl(string sUrl)
        {
            _url = sUrl;
        }

        private void ButtonCLicked()
        {
            if (isSchedule == true)
            {
                SetUrl("https://www.google.com");
                Toast.MakeText(this, "isSchedule is true", ToastLength.Short).Show();
            }
        }
    }
}

标准CSV数据示例2(&#34;纪念日&#34;):

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

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

这种方法并不好。至少不适用于CSV文件。应始终有一个表格,每行都有精确的列数。 当数据的计数总是精确的时,您可以将类转换为通用类,从通用类转换为特定数据类(用于解析器)。

非标准数据示例:(姓名和纪念日;没有标题行):

Date,Anniversary
1.1.,New year

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

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

不同方法的一个例子:

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

并保存:

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

答案 1 :(得分:0)

我知道这已经三岁了,但是我试图做同样的事情,但是我发现了一个可以使其正常工作的黑客。

csvHelper中令人反感的代码如下:

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

    return type;
}

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

GetType()