2015-12-01 26 views
3

我已經能夠使用ShouldSerialize 屬性模式與XmlSerializer忽略ICollection<>類型的屬性。以下是示例代碼。如果我在ListOfTestClassB屬性上使用XmlIgnore,但我希望使用ShouldSerialize模式實現相同的功能,則該模式有效。如果我運行下面的代碼,我得到「System.InvalidOperationException」曰:無法使ShouldSerialize模式與XmlSerializer一起使用

無法序列類型System.Collections.Generic.ICollection`1成員ConsoleApplication3.TestClassA.ListOfTestClassB [ConsoleApplication3.TestClassB,ConsoleApplication3,版本= 1.0.0.0,Culture = neutral,PublicKeyToken = null]],因爲它是一個接口。

任何人都可以突出顯示我錯過了什麼?

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.Text; 
using System.Threading.Tasks; 
using System.Xml; 
using System.Xml.Serialization; 

namespace ConsoleApplication3 
{ 
    [Serializable] 
    public class TestClassA 
    { 
     public int A { get; set; } 
     public int B { get; set; } 
     public string C { get; set; } 
     public TestClassB TestClass { get; set; } 

     public virtual ICollection<TestClassB> ListOfTestClassB { get; set; } 

     public bool ShouldSerializeListOfTestClassB() 
     { 
     return false; 
     } 
    } 

    [Serializable] 
    public class TestClassB 
    { 
     public int Int32 { get; set; } 
     public string String { get; set; } 
    } 


    class Program 
    { 
     static object GetObject() 
     { 
     return new TestClassA { A = 1, B = 2, C = "test class a", TestClass = new TestClassB { Int32 = 11, String = "test class b"} }; 
     } 
     static void Main(string[] args) 
     { 
     var result = new StringBuilder(); 
     var entity = GetObject(); 
     var ser = new XmlSerializer(entity.GetType()); 

     var settings = new XmlWriterSettings { OmitXmlDeclaration = true }; 

     using (var stream = new MemoryStream()) 
     { 
      // make a copy of the entity - we do not want to serialize ZIP file ! 
      var formatter = new BinaryFormatter(); 
      formatter.Serialize(stream, entity); 
      stream.Position = 0; 
      entity = formatter.Deserialize(stream); 
     } 

     // serialize object 
     ser.Serialize(XmlWriter.Create(result, settings), entity); 

     Console.WriteLine(result.ToString()); 
     Console.ReadLine(); 
     } 
    } 
} 

回答

4

這是檢查發生的地方:http://referencesource.microsoft.com/#System.Xml/System/Xml/Serialization/Models.cs,249

正如你所看到的,它不會調用ShouldSerialize*方法,直到它已經確定要序列化的字段/屬性之後。所以,你的ListOfTestClassB必須是可序列化的,它必須用[XmlIgnore]裝飾。爲了可序列化,您的財產必須是應用了[Serializable]屬性的具體類型。

如果您無法修改該類,則有一種解決方法。 XmlSerializer.Serialize(...)方法的一個過載接受覆蓋對象。我已經創建了一個簡單的例子:

[Serializable] 
public class Foo 
{ 
    public IList<int> Numbers { get; set; } 
    public string TheNumber { get; set; } 
} 

class Program 
{ 
    private static void Main(string[] args) 
    { 
     var attributes = new XmlAttributes 
     { 
      XmlIgnore = true 
     }; 
     var overrides = new XmlAttributeOverrides(); 
     overrides.Add(typeof(Foo), "Numbers", attributes); 

     var serializer = new XmlSerializer(typeof(Foo), overrides); 

     // the rest of this is for demo purposes. 
     // the code above is whats important 
     // 
     using (var ms = new MemoryStream()) 
     using (var reader = new StreamReader(ms)) 
     { 
      serializer.Serialize(ms, new Foo() { TheNumber = "5" });  
      ms.Flush(); 
      ms.Seek(0, SeekOrigin.Begin); 
      Debug.WriteLine(reader.ReadToEnd()); 
     } 

    } 

} 

這產生:

<?xml version="1.0"?> 
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <TheNumber>5</TheNumber> 
</Foo> 

正如你所看到的,我動態添加XmlIgnore屬性我Numbers元素,和串行因此忽略它。 :)你應該沒有任何麻煩適應你自己的代碼。

注:正如dbc指出,它緩存此串行和重要的重新使用它,否則,你將有大量的內存泄漏。您可以保留對其的靜態引用,或使用散列表爲不同類型存儲不同的序列化程序。

爲了提高性能,XML序列化基礎結構動態生成程序集以序列化和反序列化指定的類型。基礎設施找到並重新使用這些程序集。

XmlSerializer.XmlSerializer(Type) 
XmlSerializer.XmlSerializer(Type, String) 

如果你使用任何其他構造函數,可生成相同的組件的多個版本,從不卸載,從而導致內存泄漏和差:使用下面的構造函數時,纔會出現這種情況性能。最簡單的解決方案是使用前面提到的兩個構造函數之一。否則,您必須將程序集緩存在Hashtable中,如以下示例所示。

+1

該序列化程序需要被緩存,例如在靜態哈希表中,以避免嚴重的內存泄漏。請參閱[XmlSerializer類:動態生成的程序集](https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer%28v=vs.110%29.aspx) – dbc

+0

謝謝@dbc。我編輯了我的答案,以引起關注。 – Amy

+0

謝謝@Amy這看起來很有希望。我遇到了XmlAttributedOverride的使用,但ShouldSerialize patter似乎是一個簡單的實現,並提供了更多的控制,但顯然我不能使用它。我會用你的建議。非常感謝! – shigarr

0

如果您使用XmlIgnore,那麼它根本不會在意該屬性。如果使用ShouldSerialize,則直到運行時它纔會知道是否應該序列化該類型,因此它必須能夠。在這種情況下,你想要序列化的類型必須是一個具體的類。嘗試使用List<TestClassB>

+0

我無法控制課程。我將這個樣本編碼在這裏發佈。 TestClassA是EF6數據庫第一種方法生成的示例。問題是接口不可序列化,因此引發異常。我可以在這種情況下使用任何解決方法嗎?我只是簡單地希望能夠在運行時忽略此ICollection <>屬性。 – shigarr

+1

你沒有任何控制類,但你可以添加一個'ShouldSerialize *'方法嗎?我很困惑。 – Amy

+0

@shigarr看到艾米的評論 –

相關問題