无法使ShouldSerialize模式与XmlSerializer一起使用

时间:2015-12-01 16:08:49

标签: c# .net xmlserializer

我能够使用XmlSerializer的ShouldSerialize Property 模式忽略类型ICollection<>的属性。下面是示例代码。如果我在XmlIgnore属性上使用ListOfTestClassB,但我希望使用ShouldSerialize模式实现相同的功能,则它可以正常工作。如果我运行下面的代码,我得到'System.InvalidOperationException'说:

  

无法序列化System.Collections.Generic.ICollection`1 [[ConsoleApplication3.TestClassB,ConsoleApplication3,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null]]类型的成员ConsoleApplication3.TestClassA.ListOfTestClassB,因为它是一个接口

任何人都能突出我缺少的东西吗?

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();
      }
   }
}

2 个答案:

答案 0 :(得分:5)

这是检查发生的地方: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 :(得分:0)

如果您使用XmlIgnore,那么它根本不关心该属性。如果使用ShouldSerialize,它在运行时才知道是否应该序列化该类型,因此它必须能够。在这种情况下,您尝试序列化的类型必须是具体类。尝试使用List<TestClassB>