C# 无法使ShouldSerialize模式与XmlSerializer一起工作

C# 无法使ShouldSerialize模式与XmlSerializer一起工作,c#,.net,xmlserializer,C#,.net,Xmlserializer,我已经能够使用ShouldSerializeProperty模式和XmlSerializer忽略类型为ICollection的属性。下面是示例代码。如果我在ListOfTestClassB属性上使用XmlIgnore,它会起作用,但我希望利用ShouldSerialize模式实现相同的功能。如果我运行下面的代码,我会得到“System.InvalidOperationException”,上面说: 无法序列化System.Collections.Generic.ICollection`1[[Co

我已经能够使用ShouldSerializeProperty模式和
XmlSerializer
忽略类型为
ICollection
的属性。下面是示例代码。如果我在
ListOfTestClassB
属性上使用
XmlIgnore
,它会起作用,但我希望利用
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();
      }
   }
}
使用系统;
使用System.Collections.Generic;
使用System.IO;
使用System.Linq;
使用System.Runtime.Serialization.Formatters.Binary;
使用系统文本;
使用System.Threading.Tasks;
使用System.Xml;
使用System.Xml.Serialization;
命名空间控制台应用程序3
{
[可序列化]
公共类TestClassA
{
公共int A{get;set;}
公共int B{get;set;}
公共字符串C{get;set;}
公共TestClassB TestClass{get;set;}
公共虚拟ICollection ListOfTestClassB{get;set;}
公共bool应该序列化ListofTestClassB()
{
返回false;
}
}
[可序列化]
公共类TestClassB
{
公共int Int32{get;set;}
公共字符串{get;set;}
}
班级计划
{
静态对象GetObject()
{
返回新的TestClassA{A=1,B=2,C=“TestClassA”,TestClass=newtestclassb{Int32=11,String=“TestClassB”};
}
静态void Main(字符串[]参数)
{
var result=新的StringBuilder();
var entity=GetObject();
var ser=新的XmlSerializer(entity.GetType());
var设置=新的XmlWriterSettings{OmitXmlDeclaration=true};
使用(var stream=new MemoryStream())
{
//制作实体的副本-我们不想序列化ZIP文件!
var formatter=新的二进制格式化程序();
序列化(流、实体);
流位置=0;
实体=格式化程序。反序列化(流);
}
//序列化对象
序列化(XmlWriter.Create(结果、设置)、实体);
Console.WriteLine(result.ToString());
Console.ReadLine();
}
}
}

如果使用
XmlIgnore
,则它根本不关心该属性。如果使用
ShouldSerialize
,则直到运行时它才知道是否应该序列化该类型,因此它必须能够。在这种情况下,您试图序列化的类型必须是一个具体的类。尝试使用
列表

这是检查发生的地方:

如您所见,它不会调用
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());
        }

    }

}
如果使用任何其他构造函数,将生成同一程序集的多个版本,并且从未卸载,这将导致内存泄漏和性能低下。最简单的解决方案是使用前面提到的两个构造函数之一。否则,必须将程序集缓存在哈希表中,如以下示例所示


我无法控制课堂。我把这个样本编码到这里。TestClassA是EF6 database first方法生成的示例。问题是接口不可序列化,因此会引发异常。在这种情况下,我可以使用哪些变通方法?我只是希望能够在运行时忽略此ICollection属性。您对该类没有任何控制权,但可以向其添加
ShouldSerialize*
方法?“我搞糊涂了。”希加尔看到艾米的了吗comment@Amy我正在考虑使用分部类来添加该功能。该序列化程序需要缓存,例如在静态哈希表中,以避免严重的内存泄漏。见谢谢@dbc。我编辑了我的答案以引起大家的注意。谢谢@Amy,这看起来很有希望。我遇到过使用XmlAttributeOverride,但ShouldSerialize模式似乎是一个简单的实现,并提供了更多的控制,但显然我不能使用它。我会采纳你的建议。谢谢!
<?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>
XmlSerializer.XmlSerializer(Type)
XmlSerializer.XmlSerializer(Type, String)