1

我有以下记录(为简洁起见减少了):

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

因此,示例记录可能是: John, Smith, 1|2|3|4

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

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

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

res.ToArray(ArrayType);

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

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

4

3 回答 3

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();
    }
}
于 2012-06-25T08:29:53.220 回答
2

另一种方法是处理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();
    }
}
于 2012-06-25T09:24:49.733 回答
2

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

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

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

因此,长话短说,将数组更改为列表就足够了。

代替:

public int[] TestProperty;   

变成:

public List<int> TestProperty;   

并修改自定义转换器以返回列表:

return from.Split('|').Select(x => Convert.ToInt32(x)).ToList();
于 2016-06-30T11:30:08.027 回答