2010-10-19 9 views
8

我試圖將JSon文件反序列化爲包含抽象列表的類的實例。將實例序列化到Json效果很好(請查看下面的json文件)。當反序列化時,我得到一個「System.MemberAccessException」消息「無法創建抽象類」。可以想象的是,deseralizer正試圖實例化抽象類而不是具體類。使用DataContractJsonSerializer對抽象列表JSON去串聯

在我的例子反序列化的類被稱爲ElementContainer:

namespace Data 
{ 
    [DataContract] 
    [KnownType(typeof(ElementA))] 
    [KnownType(typeof(ElementB))] 
    public class ElementContainer 
    { 
     [DataMember] 
     public List<Element> Elements { get; set; } 
    } 

    [DataContract] 
    public abstract class Element 
    { 
    } 

    [DataContract] 
    public class ElementA : Element 
    { 
     [DataMember] 
     int Id { get; set; } 
    } 

    [DataContract] 
    public class ElementB : Element 
    { 
     [DataMember] 
     string Name { get; set; } 
    } 
} 

這是連載的JSON文件和我試圖反序列化。請注意「__type」字段解串器來創建具體類:

{ 
    "Elements": 
    [ 
     { 
      "__type":"ElementA:#Data", 
      "Id":1 
     }, 
     { 
      "__type":"ElementB:#Data", 
      "Name":"MyName" 
     }  
    ] 
} 

是代碼我使用的反序列化下列內容:

public T LoadFromJSON<T>(string filePath) 
    { 
     try 
     { 
      using (FileStream stream = File.OpenRead(filePath)) 
      { 
       DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); 
       T contract = (T)serializer.ReadObject(stream); 
       return contract; 
      } 
     } 
     catch (System.Exception ex) 
     { 
      logger.Error("Cannot deserialize json " + filePath, ex); 
      throw; 
     } 
    } 

有可能使反序列化工作?

謝謝!

+0

您是否嘗試將列表的類型更改爲對象並查看發生了什麼? – leppie 2010-10-19 18:13:56

+0

我試過了,但它沒有改變任何東西。 – noon 2010-10-19 18:55:44

回答

10

我們發現它不工作的原因。在對象序列化之後,我們將結果字符串標識爲更易讀。然後我們寫串入一個文件:

public void SaveContractToJSON<T>(T contract, string filePath) 
    { 
     using (MemoryStream stream = new MemoryStream()) 
     { 
      DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); 
      serializer.WriteObject(stream, contract); 
      string json = Encoding.UTF8.GetString(stream.ToArray()); 
      File.WriteAllText(filePath, json.IndentJSON()); 
     } 
    } 

的identation實際上就是反序列化是不工作的原因。看來DataContractJsonSerializer的解析器真的很挑剔。如果某些字符位於字符{和字段「__type」之間,則序列化程序將丟失。

例如這個字符串將正確序列:

"{\"Elements\":[{\"__type\":\"ElementA:#Data\",\"Id\":1}]}" 

但這個下一個字符串將不序列。

"{\"Elements\":[ {\"__type\":\"ElementA:#Data\",\"Id\":1}]}" 

唯一的區別是「__type」之前的空格字符。序列化將拋出MemberAccessException。這是誤導性的,因爲只有在反序列化爲抽象List時纔會出現此行爲。序列化到抽象領域工作正常,無論字符。

要解決此問題而不刪除文件的可讀性,可以在脫機之前修改該字符串。例如:

public T LoadContractFromJSON<T>(string filePath) 
    { 
     try 
     { 
      string text = File.ReadAllText(filePath); 
      text = Regex.Replace(text, "\\{[\\n\\r ]*\"__type", "{\"__type"); 
      using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(text))) 
      { 
       DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T)); 
       T contract = (T)serializer.ReadObject(stream); 
       return contract; 
      } 
     } 
     catch (System.Exception ex) 
     { 
      logger.Error("Cannot deserialize json " + filePath, ex); 
      throw; 
     } 
    } 
+0

發佈的'\\ {[\\ n \\ r] * \「__ type'模式是危險的,它會區分'__type'之前序列化的任何屬性或拾取包含文本''__type'的任何屬性,以及使用隱式空間而不是明確的'\ s'來處理空白和特定於平臺的換行符序列。 – 2010-10-19 23:05:29

+0

__type就像DataContractJsonSerializer的關鍵字。它必須放在任何其他字段之前(並且實際上在任何其他字符之前),否則json不會以正確的類型進行序列化。關於平板特定字符,我會改變這種情況。謝謝。 – noon 2010-10-21 03:21:56

+0

只需將__type作爲對象的第一個屬性爲我工作。在反序列化之前不需要用正則表達式替換。 – 2013-10-16 20:04:06

相關問題