我能够使用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();
}
}
}
答案 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>
。