.NET XML序列化陷阱?
在进行CXML序列化时,我遇到了一些陷阱
我以为我会分享:
- 我们无法序列化只读项(例如KeyValuePairs)
- 我们无法序列化通用词典。相反,请尝试以下包装器类(来自http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx):
using System; using System.Collections.Generic; using System.Text; using System.Xml.Serialization; [XmlRoot("dictionary")] public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable { public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) return; while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { reader.ReadStartElement("item"); reader.ReadStartElement("key"); TKey key = (TKey)keySerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadStartElement("value"); TValue value = (TValue)valueSerializer.Deserialize(reader); reader.ReadEndElement(); this.Add(key, value); reader.ReadEndElement(); reader.MoveToContent(); } reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { XmlSerializer keySerializer = new XmlSerializer(typeof(TKey)); XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue)); foreach (TKey key in this.Keys) { writer.WriteStartElement("item"); writer.WriteStartElement("key"); keySerializer.Serialize(writer, key); writer.WriteEndElement(); writer.WriteStartElement("value"); TValue value = this[key]; valueSerializer.Serialize(writer, value); writer.WriteEndElement(); writer.WriteEndElement(); } } }
还有其他XML序列化陷阱吗?
解决方案
回答
专用变量/属性未在XML序列化的默认机制中序列化,而是在二进制序列化中进行了序列化。
回答
哦,这是一个很好的例子:由于XML序列化代码已生成并放置在单独的DLL中,所以当代码中出现错误而使序列化程序中断时,我们将不会收到任何有意义的错误。就像"无法找到s3d3fsdf.dll"之类的东西。好的。
回答
Private variables/properties are not serialized in XML serialization, but are in binary serialization.
我相信,如果通过公共属性公开私有成员,私有成员也不会序列化,那么公共成员都将引用空值,这也会为我们带来好处。
回答
我还不能发表评论,所以我将评论Dr8k的帖子,并再进行一次观察。私有变量公开为公共getter / setter属性,并通过这些属性进行序列化/反序列化。我们是在以前的旧工作中做到这一点的。
但是要注意的一件事是,如果这些属性中有任何逻辑,则逻辑会运行,因此有时,序列化顺序实际上很重要。成员是按代码中的顺序进行隐式排序的,但是并不能保证,尤其是当我们继承另一个对象时。明确订购它们是后方的痛苦。
过去我一直为此感到疲倦。
回答
通过yield return生成的IEnumerables <T>
不可序列化。这是因为编译器会生成一个单独的类来实现收益回报,并且该类未标记为可序列化的。
回答
如果XML序列化生成的程序集与尝试使用它的代码不在相同的Load上下文中,则会遇到诸如以下的错误:
System.InvalidOperationException: There was an error generating the XML document. ---System.InvalidCastException: Unable to cast object of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at Microsoft.Xml.Serialization.GeneratedAssembly. XmlSerializationWriterSettings.Write3_Settings(Object o)
对我而言,原因是使用LoadFrom上下文加载的插件,与使用Load上下文相比有许多缺点。跟踪下来很有趣。
回答
另一个巨大的难题:当通过网页(ASP.NET)输出XML时,我们不想包含Unicode字节顺序标记。当然,使用或者不使用BOM的方式几乎相同:
不良(包括BOM):
XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);
好的:
XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))
我们可以显式传递false来指示我们不需要BOM。请注意,Encoding.UTF8
和UTF8Encoding
之间存在明显的明显差异。
开头的三个额外BOM字节为(0xEFBBBF)或者(239 187 191)。
参考:http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/
回答
我不能真正解释这一点,但是我发现它不会序列化:
[XmlElement("item")] public myClass[] item { get { return this.privateList.ToArray(); } }
但这将:
[XmlElement("item")] public List<myClass> item { get { return this.privateList; } }
同样值得注意的是,如果要序列化到内存流,则可能需要先使用0。
回答
如果XSD使用替换组,则可能无法自动对其进行反序列化。我们需要编写自己的序列化程序来处理这种情况。
例如。
<xs:complexType name="MessageType" abstract="true"> <xs:attributeGroup ref="commonMessageAttributes"/> </xs:complexType> <xs:element name="Message" type="MessageType"/> <xs:element name="Envelope"> <xs:complexType mixed="false"> <xs:complexContent mixed="false"> <xs:element ref="Message" minOccurs="0" maxOccurs="unbounded"/> </xs:complexContent> </xs:complexType> </xs:element> <xs:element name="ExampleMessageA" substitutionGroup="Message"> <xs:complexType mixed="false"> <xs:complexContent mixed="false"> <xs:attribute name="messageCode"/> </xs:complexContent> </xs:complexType> </xs:element> <xs:element name="ExampleMessageB" substitutionGroup="Message"> <xs:complexType mixed="false"> <xs:complexContent mixed="false"> <xs:attribute name="messageCode"/> </xs:complexContent> </xs:complexType> </xs:element>
在此的示例信封可以包含消息。但是,.NET的默认序列化程序无法区分Message,ExampleMessageA和ExampleMessageB。它只会与基本Message类之间进行序列化。
回答
在没有显式序列化的情况下,小心地序列化类型,这可能会导致.Net构建它们时出现延迟。我最近在序列化RSAParameters时发现了这一点。
回答
我们不能序列化只读属性。即使我们从未打算使用反序列化将XML转换为对象,也必须具有getter和setter。
出于同样的原因,我们无法序列化返回接口的属性:反序列化器将不知道要实例化的具体类。
回答
我们可能会遇到序列化Color和/或者Font类型的对象的问题。
以下是对我有帮助的建议:
http://www.codeproject.com/KB/XML/xmlsettings.aspx
http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx
回答
需要注意的另一件事:如果使用"默认" XML序列化,则不能序列化私有/受保护的类成员。
但是,我们可以在类中指定实现IXmlSerializable的自定义XML序列化逻辑,并序列化我们需要/想要的任何私有字段。
http://msdn.microsoft.com/zh-CN/library/system.xml.serialization.ixmlserializable.aspx
回答
从内存流序列化为XML字符串时,请确保使用MemoryStream#ToArray()而不是MemoryStream#GetBuffer(),否则我们将得到无法正确反序列化的垃圾字符(由于分配了额外的缓冲区)。
http://msdn.microsoft.com/zh-cn/library/system.io.memorystream.getbuffer(VS.80).aspx
回答
无法序列化没有无参数构造函数的对象(只是被该对象咬住了)。
并且由于某些原因,从以下属性中,Value被序列化,但是没有FullName:
public string FullName { get; set; } public double Value { get; set; }
我从来没有想过为什么,我只是将Value更改为Internal ...
回答
如果序列化程序遇到类型为接口的成员/属性,则不会序列化。例如,以下代码不会序列化为XML:
public class ValuePair { public ICompareable Value1 { get; set; } public ICompareable Value2 { get; set; } }
尽管这将序列化:
public class ValuePair { public object Value1 { get; set; } public object Value2 { get; set; } }