2012-06-19 19 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();