使用CSVHelper如何反序列化带有子项列表的CSV [英] Using CSVHelper how to deserialise an CSV with a list of Sub Item

查看:112
本文介绍了使用CSVHelper如何反序列化带有子项列表的CSV的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出对象 FooBar ,该对象包含 Bar ,其中 FooBar Bar 定义如下:

Given to object FooBar that contains a List of Bar, with FooBar and Bar define as such:

class FooBar{
    int FooID {get;set;}
    string FooProperty1 {get;set;}
    List<Bar> Bars {get;set;};
}

class Bar{
    int BarID {get;set;}    
    string BarProperty1 {get;set;}  
    string BarProperty2 {get;set;}  
    string BarProperty3 {get;set;}
}

以下CSV作为输入:

1,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2

其中字段BarID,BarProperty1,BarProperty2,BarProperty3的后缀是集合中的索引。

Where the field BarID, BarProperty1, BarProperty2, BarProperty3 are suffixed by their indice in the collection.

如何将此输入反序列化到对象中?

How do I deserialise this input into my object?

1个FooBar实例和2个子Bar: 1,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1, BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2

1 instance of FooBar, and 2 sub Bar: 1,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2

1个FooBar实例但没有Bar:

1,FooProperty1

1 instance of FooBar but no Bar:
1,FooProperty1


我尝试使用Convert将这些属性映射到Bar的新实例,例如:

I have try using Convert in order to map those property to a new instance of Bar like :

public class FooBarMap : ClassMap<FooBar> 
{
    public FooBarMap()
    {
        Map(m => m.FooID);
        Map(m => m.Bars).ConvertUsing(row =>
        {            
            var list = new List<Bar>
            {
                new Bar { 
                    BarProperty1 = row.GetField("BarProperty1_1"), 
                    BarProperty2 = row.GetField("BarProperty2_1"),
                    // .. Other Properties
                },
                new Bar {}, //.. Same on _2
            };
            return list;
        });
    }
}

当然,无法控制输入。我本来会发送Json / Xml而不是CSV文件。

Of course no control over the input. I would have been sending Json/Xml not CSV.

推荐答案

自定义类型转换器可能

您需要使用 Index 属性装饰该属性(即使未使用)

You need to decorate the property with an Index attribute (even though it's not used)

public class FooBar
{
    [Index(2)]
    public List<Bar> Bars { get; set; }
}

转换器用于读取和写入,因此您需要重写两种方法:

The converter is used for both reading and writing, so you need to override two methods:

  public class BarListConverter : DefaultTypeConverter
  {
    public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
    {
      var list = new List<Bar>();
      if (text == null) return list;
      do
      {
        var barIndex = list.Count + 1;
        var bar = new Bar
        {
          BarID = row.GetField<int>($"BarID_{barIndex}"),
          BarProperty1 = row.GetField<string>($"BarProperty1_{barIndex}"),
          BarProperty2 = row.GetField<string>($"BarProperty2_{barIndex}"),
          BarProperty3 = row.GetField<string>($"BarProperty3_{barIndex}")
        };
        list.Add(bar);
      } while (row.Context.CurrentIndex > 0 && row.Context.CurrentIndex < row.Context.Record.Length - 1);
      return list;
    }

    public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
    {
      var bars = value as List<Bar>;
      if (bars == null) return null;
      foreach (var bar in bars)
      {
        row.WriteField(bar.BarID);
        row.WriteField(bar.BarProperty1);
        row.WriteField(bar.BarProperty2);
        row.WriteField(bar.BarProperty3);
      }
      return null;
    }
  }
}

阅读:

  public List<FooBar> Reading()
  {
    using (var stream = new MemoryStream())
    using (var writer = new StreamWriter(stream))
    using (var reader = new StreamReader(stream))
    using (var csv = new CsvReader(reader))
    {
      writer.WriteLine(
        "FooID,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2");
      writer.WriteLine("1,Foo1,1,2,3,4,5,6,7,8");
      writer.Flush();
      stream.Position = 0;

      csv.Configuration.HeaderValidated = null;
      csv.Configuration.MissingFieldFound = null;
      csv.Configuration.TypeConverterCache.AddConverter<List<Bar>>(new BarListConverter());

      return csv.GetRecords<FooBar>().ToList();
    }
  }

写作:

  public string Writing(List<FooBar> data)
  {
    using (var stream = new MemoryStream())
    using (var writer = new StreamWriter(stream))
    using (var reader = new StreamReader(stream))
    using (var csv = new CsvWriter(writer))
    {
      csv.Configuration.HasHeaderRecord = false;
      csv.Configuration.TypeConverterCache.AddConverter<List<Bar>>(new BarListConverter());
      csv.WriteRecords(data);
      writer.Flush();
      stream.Position = 0;

      return reader.ReadToEnd();
    }

这篇关于使用CSVHelper如何反序列化带有子项列表的CSV的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆