2012-06-19 18 views
0

我有以下记录(减少为了简洁):FileHelpers不能转换的字段映射到目的地阵列

[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个元素。

任何人都可以提出,如果我这样做是错误的或可能的解决?

回答

1

另一种方法是,以处理解析在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(); 
    } 
} 
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] 
    [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(); 
    } 
} 
+0

嗨,真的很感谢这个你的帮助。我的例子比我的实际问题简单一点,我需要能够从我的转换器返回。我的实际应用程序有多个需要导入的注释,它们使用单​​个和双个管道字符,例如2011-01-01T10:00 |注1 || 2011-02-01T11:00 |注2 ||等等...你认为有这个解决方案吗? – jaffa

+0

如果这很复杂,将'TestProperty'更改为一个普通的'String',然后在生成的'ImportRecords'上执行'foreach'可能会更容易,以便进一步解析TestProperty。但看到我的答案另一种方法。 – shamp00

0

对不起,碰到这个老问题,但我只是有同样的问题,我发现这个问题更简单的解决方案。

似乎FileHelpers处理阵列在特殊的方式,预计值与DelimitedRecord属性表示分隔分隔。因此它会尝试在每个分隔记录上调用自定义转换器,并期望您返回元素类型。

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

所以,长话短说,这是足以改变数组列表。

相反的:

public int[] TestProperty; 

更改为:

public List<int> TestProperty; 

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

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