【问题标题】:FileHelpers cannot map converted field into destination arrayFileHelpers 无法将转换后的字段映射到目标数组
【发布时间】:2012-06-19 09:12:55
【问题描述】:

我有以下记录(为简洁而减少):

 [DelimitedRecord(",")]
    [IgnoreFirst]
    [IgnoreEmptyLines()]
    public class ImportRecord
    {
        [FieldQuoted]
        [FieldTrim(TrimMode.Both)]
        public string FirstName;

        [FieldQuoted]
        [FieldTrim(TrimMode.Both)]
        public string LastName;

        [FieldQuoted]
        [FieldTrim(TrimMode.Both)]
        [FieldOptional]
        [FieldConverter(typeof(TestPropertyConverter))]
        public int[] TestProperty;    
    }

转换器代码:

  public class TestPropertyConverter : ConverterBase
    {
        public override object StringToField(string from)
        {
              var ret = from.Split('|').Select(x => Convert.ToInt32(x)).ToArray();
              return ret;
        }
    }

因此,示例记录可能是: 约翰,史密斯,1|2|3|4

它会期望值 1、2、3、4 扩展并填充 TestProperty 数组。但是,我收到以下异常:

源数组中的至少一个元素无法转换为目标数组类型。

我尝试调试代码,但它似乎在 FieldBase.cs 内的 ExtractFieldValue() 函数中爆炸,它试图从函数中返回。 以下行似乎是罪魁祸首:

res.ToArray(ArrayType);

似乎期望“res”变量是目标类型数组,但它包含数组本身的 1 个元素。

谁能建议我是否做错了或可能的解决方法?

【问题讨论】:

    标签: c# .net parsing csv filehelpers


    【解决方案1】:

    很抱歉提出这个老问题,但我也遇到了同样的问题,我找到了一个更简单的解决方案。

    似乎 FileHelpers 以特殊方式处理数组,并期望值用DelimitedRecord 属性指示的分隔符分隔。因此,它尝试在每个分隔记录上调用自定义转换器,并希望您返回元素类型。

    在我的情况下,我有位图 (010100101001010101001010001101) 字段,没有任何我想转换为 bool[] 的分隔符。我进入源代码发现typeof(T).IsArray用于确定这种行为。

    所以,长话短说,将数组变为列表就足够了。

    代替:

    public int[] TestProperty;   
    

    改成:

    public List<int> TestProperty;   
    

    并且还修改自定义转换器以返回一个列表:

    return from.Split('|').Select(x => Convert.ToInt32(x)).ToList();
    

    【讨论】:

      【解决方案2】:

      您不需要编写转换器。您只需要更改最后一个字段的字段分隔符。以下示例有效:

      [DelimitedRecord(",")]
      //[IgnoreFirst]
      [IgnoreEmptyLines()]
      public class ImportRecord
      {
          [FieldQuoted]
          [FieldTrim(TrimMode.Both)]
          public string FirstName;
      
          [FieldQuoted]
          [FieldTrim(TrimMode.Both)]
          public string LastName;
      
          [FieldQuoted]
          [FieldTrim(TrimMode.Both)]
          [FieldOptional]
          [FieldDelimiter("|")] // <-- here 
          public int[] TestProperty;
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              var engine = new FileHelperEngine<ImportRecord>();
              string fileAsString = @"John, Smith, 1|2|3|4" + Environment.NewLine;
              ImportRecord[] validRecords = engine.ReadString(fileAsString);
              Assert.AreEqual("John", validRecords[0].FirstName);
              Assert.AreEqual("Smith", validRecords[0].LastName);
              Assert.AreEqual(new int[] { 1, 2, 3, 4 }, validRecords[0].TestProperty);
              Console.ReadKey();
          }
      }
      

      【讨论】:

      • 您好,非常感谢您的帮助。我的例子比我的实际问题简单一点,我需要能够从我的转换器返回。我的实际应用程序有多个需要导入的注释,它们使用单​​管道和双管道字符,例如2011-01-01T10:00|注1||2011-02-01T11:00|注2||等等...您认为有解决方案吗?
      • 如果它那么复杂,将TestProperty 更改为普通的String 然后在结果ImportRecords 上执行foreach 以进一步解析TestProperty 可能会更容易.但是请参阅我的其他答案以了解另一种方法。
      【解决方案3】:

      另一种方法是在 AfterReadRecord 事件中处理解析。当数组字段的解析很复杂时,这很有用。 (自定义转换器功能较弱,不会给您,例如,e.SkipThisRecord)。

      [DelimitedRecord(",")]
      //[IgnoreFirst]
      [IgnoreEmptyLines()]
      public class ImportRecord
      {
          [FieldQuoted]
          [FieldTrim(TrimMode.Both)]
          public string FirstName;
      
          [FieldQuoted]
          [FieldTrim(TrimMode.Both)]
          public string LastName;
      
          [FieldQuoted]
          [FieldTrim(TrimMode.Both)]
          [FieldOptional]
          public string TestProperty;
      
          [FieldIgnored] // <-- this field is ignored by the FileHelpers engine
          public int[] ParsedTestProperty; 
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              var engine = new FileHelperEngine<ImportRecord>();
              engine.AfterReadRecord += engine_AfterReadRecord;
      
              string fileAsString = @"John, Smith, 1|2|3|4" + Environment.NewLine;
              ImportRecord[] validRecords = engine.ReadString(fileAsString);
              Assert.AreEqual("John", validRecords[0].FirstName);
              Assert.AreEqual("Smith", validRecords[0].LastName);
              Assert.AreEqual(new int[] { 1, 2, 3, 4 }, validRecords[0].ParsedTestProperty);
              Console.ReadKey();
          }
      
          static void engine_AfterReadRecord(EngineBase engine, AfterReadEventArgs<ImportRecord> e)
          {
              e.Record.ParsedTestProperty = e.Record.TestProperty.Split('|').Select(x => Convert.ToInt32(x)).ToArray();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-16
        • 2014-09-12
        • 1970-01-01
        • 2021-10-10
        • 1970-01-01
        • 1970-01-01
        • 2021-07-21
        相关资源
        最近更新 更多