Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用CSVHelper如何使用子项列表反序列化CSV 问题:_C#_Csv_Csvhelper - Fatal编程技术网

C# 使用CSVHelper如何使用子项列表反序列化CSV 问题:

C# 使用CSVHelper如何使用子项列表反序列化CSV 问题:,c#,csv,csvhelper,C#,Csv,Csvhelper,给定对象FooBar,该对象包含Bar列表,其中FooBar和Bar定义如下: 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 BarPrope

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

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;}
}
其中,字段BarID、BarProperty1、BarProperty2、BarProperty3以其在集合中的标记作为后缀

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


输入示例: 1个FooBar实例和2个子栏:
1、FooProperty1、BarID_1、BarProperty1、BarProperty2_1、BarProperty3_1、BarID_2、BarProperty1_2、BarProperty2、BarProperty3_2

1个FooBar实例,但没有Bar:
1,食品属性1


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

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;
        });
    }
}
公共类FooBarMap:ClassMap
{
公共FooBarMap()
{
Map(m=>m.FooID);
映射(m=>m.bar)。转换使用(行=>
{            
变量列表=新列表
{
新酒吧{
BarProperty1=row.GetField(“BarProperty1_1”),
BarProperty2=row.GetField(“BarProperty2_1”),
//…其他财产
},
新条{},//…在_2上相同
};
退货清单;
});
}
}

当然,对输入没有控制权。我本来应该发送Json/Xml而不是CSV。

使用自定义类型转换器是可能的,但需要技巧

您需要使用
索引
属性装饰属性(即使未使用)

公共类FooBar
{
[索引(2)]
公共列表栏{get;set;}
}
转换器用于读取和写入,因此需要重写两种方法:

  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;
    }
  }
}
公共类BarListConverter:DefaultTypeConverter
{
公共重写对象ConvertFromString(字符串文本、IReaderRow行、MemberMapData MemberMapData)
{
var list=新列表();
if(text==null)返回列表;
做
{
var barIndex=list.Count+1;
变量条=新条
{
BarID=row.GetField($“BarID{barIndex}”),
BarProperty1=row.GetField($“BarProperty1{barIndex}”),
BarProperty2=row.GetField($“BarProperty2{barIndex}”),
BarProperty3=row.GetField($“BarProperty3{barIndex}”)
};
列表。添加(条);
}while(row.Context.CurrentIndex>0&&row.Context.CurrentIndex
阅读:

  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();
    }
  }
公共列表读取()
{
使用(var stream=new MemoryStream())
使用(var writer=新的StreamWriter(流))
使用(变量读取器=新的流读取器(流))
使用(var csv=新的CsvReader(读卡器))
{
作家,作家(
“食品ID,食品属性1,BarID_1,BarProperty 1_1,BarProperty 2_1,BarProperty 3_1,BarID_2,BarProperty 1_2,BarProperty 2_2,BarProperty 3_2”);
writer.WriteLine(“1,1,2,3,4,5,6,7,8”);
writer.Flush();
流位置=0;
csv.Configuration.HeaderValidated=null;
csv.Configuration.MissingFieldFound=null;
csv.Configuration.TypeConverterCache.AddConverter(新的BarListConverter());
返回csv.GetRecords().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();
    }
公共字符串写入(列表数据)
{
使用(var stream=new MemoryStream())
使用(var writer=新的StreamWriter(流))
使用(变量读取器=新的流读取器(流))
使用(var csv=新csv编写器(编写器))
{
csv.Configuration.HasHeaderRecord=false;
csv.Configuration.TypeConverterCache.AddConverter(新的BarListConverter());
csv.写入记录(数据);
writer.Flush();
流位置=0;
返回reader.ReadToEnd();
}

我制作了一个非常简化的方法,可以迭代键/值对

private static FooBar Parse(string value)
{
    // a basic check for null or empty string
    if (String.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));

    // split the string
    string[] split = value.Split(',');

    // a basic check for properly formed key/value pairs in the string
    if (split.Length < 2 || split.Length % 2 != 0)
        throw new ArgumentException("Malformed string.", nameof(value));

    // put the values into our object
    var result = new FooBar();

    // Foo pair
    result.FooID = Int32.Parse(split[0]);
    result.FooProperty1 = split[1];

    // Bar pairs
    result.Bars = new List<Bar>();
    for (int i = 2; i < split.Length; i = i + 4)
    {
        result.Bars.Add(new Bar()
        {
            BarID = split[i],
            BarProperty1 = split[i+1],
            BarProperty2 = split[i+2],
            BarProperty3 = split[i+3]
        });
    }

    return result;
}

请注意,您的类需要稍微更新到

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

public class Bar
{
    public string BarID { get; set; } // you originally had this as int
    public string BarProperty1 { get; set; }
    public string BarProperty2 { get; set; }
    public string BarProperty3 { get; set; }
}
公共类FooBar
{
公共int FooID{get;set;}
公共字符串FooProperty1{get;set;}
公共列表栏{get;set;}
}
公共类酒吧
{
公共字符串BarID{get;set;}//您最初将其作为int
公共字符串BarProperty1{get;set;}
公共字符串BarProperty2{get;set;}
公共字符串BarProperty3{get;set;}
}

对我来说,这个选项似乎没有那么复杂,而且总体上更容易阅读。我不认为有任何理由必须通过其他方式(如使用CSVHelper类)强制转换。很抱歉使用Json.et而不是CSV Helper和错误的标记。这一定是星期五。作为一个数组,bar属性不是更好吗你的C#模型,而不是单个属性?看起来它们都是相同的相关项type@Fubo,只是简化后的一个输入错误。别介意。我会尽快编辑。@ADyson,他们是name,cust,org,我刚刚将他们重命名为[ClassName]property[Number]。它不是一个Dict或字符串数组。它们不都是字符串。对于mcve来说,字符串更容易,但属性名称不是动态的。我知道它们(20+)并且模式总是“[KnowPropertyName]\u[Index+1]”。这是一个典型的问题,不容易找到解决方案。我希望赏金会让这个答案更直观。我的示例中的Csv是干净和简化的。但它是来自真实客户的真实Csv。这意味着Csv助手是解析的必备工具。多行数据、转义列分隔符(例如:
“foo;”;“foo,bar”
).但这种简化将有利于
private static FooBar Parse(string value)
{
    // a basic check for null or empty string
    if (String.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));

    // split the string
    string[] split = value.Split(',');

    // a basic check for properly formed key/value pairs in the string
    if (split.Length < 2 || split.Length % 2 != 0)
        throw new ArgumentException("Malformed string.", nameof(value));

    // put the values into our object
    var result = new FooBar();

    // Foo pair
    result.FooID = Int32.Parse(split[0]);
    result.FooProperty1 = split[1];

    // Bar pairs
    result.Bars = new List<Bar>();
    for (int i = 2; i < split.Length; i = i + 4)
    {
        result.Bars.Add(new Bar()
        {
            BarID = split[i],
            BarProperty1 = split[i+1],
            BarProperty2 = split[i+2],
            BarProperty3 = split[i+3]
        });
    }

    return result;
}
string value = "1,FooProperty1";
FooBar result = Parse(value); // works
string value = "1,FooProperty1,BarID_1,BarProperty1_1,BarProperty2_1,BarProperty3_1,BarID_2,BarProperty1_2,BarProperty2_2,BarProperty3_2";
FooBar result = Parse(value); // works
public class FooBar
{
    public int FooID { get; set; }
    public string FooProperty1 { get; set; }
    public List<Bar> Bars { get; set; }
}

public class Bar
{
    public string BarID { get; set; } // you originally had this as int
    public string BarProperty1 { get; set; }
    public string BarProperty2 { get; set; }
    public string BarProperty3 { get; set; }
}