.NET的XML序列化陷阱?
我在做C#XML序列化时遇到了一些问题,我想分享一下:
- 您不能序列化只读的项目(如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序列化在那里?
另一个巨大的问题是:通过网页(ASP.NET)输出XML时,您不希望包含Unicode字节顺序标记 。 当然,使用或不使用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/
我还不能发表评论,所以我会对Dr8k的post发表评论,再作一个观察。 作为public getter / setter属性公开的私有variables,并通过这些属性获得序列化/反序列化。 我们一直在做我以前的工作。
有一件事要注意,如果你在这些属性中有逻辑,逻辑运行,所以有时序列化的顺序实际上很重要。 成员通过在代码中如何sorting来隐式sorting,但是没有保证,特别是当您inheritance另一个对象时。 明确命令他们是后面的痛苦。
过去我被烧了
当从一个内存stream序列化到一个XMLstring时,一定要使用MemoryStream#ToArray()而不是MemoryStream#GetBuffer(),否则你将最终得到不会正确反序列化的垃圾字符(因为分配了额外的缓冲区)。
http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(VS.80).aspx
通过yield返回的IEnumerables<T>
不可序列化。 这是因为编译器生成一个单独的类来实现yield return,并且该类不被标记为可序列化。
您不能序列化只读属性。 即使您从不打算使用反序列化将XML转换为对象,您也必须拥有getter和setter。
出于同样的原因,你不能序列化返回接口的属性:反序列化器不知道要实例化的具体类。
如果序列化程序遇到具有作为其types的接口的成员/属性,则不会序列化。 例如,以下内容不会序列化为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; } }
噢,这里是一个很好的例子:由于XML序列化代码是生成并放置在单独的DLL中的,因此当代码出现中断序列化程序的错误时,不会得到任何有意义的错误。 只是像“无法finds3d3fsdf.dll”。 尼斯。
还有一点需要注意:如果使用“默认”XML序列化,则不能序列化私有/受保护的类成员。
但是,您可以在您的类中指定实现IXmlSerializable的自定义XML序列化逻辑,并序列化您需要的任何专用字段。
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
不能序列化一个没有无参数的构造器的对象(刚刚被这个构件咬了)。
由于某种原因,从以下属性中,值被序列化,但不是FullName:
public string FullName { get; set; } public double Value { get; set; }
我从来没有弄清楚为什么,我只是把价值改为内部…
如果您的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上下文有许多缺点。 相当有趣的跟踪一个下来。
序列化Color和/或Fonttypes的对象可能会遇到问题。
以下是对我的帮助:
http://www.codeproject.com/KB/XML/xmlsettings.aspx
http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx
有关XML序列化程序支持的内容的详细信息,以及有关支持的XSDfunction支持方式的详细信息,请参阅“ 高级XMLscheme定义语言属性绑定支持 ”。
如果试图序列化包含T
的子类的实例的数组List<T>
或IEnumerable<T>
,则需要使用XmlArrayItemAttribute列出所有正在使用的子types。 否则,当您序列化时,您将在运行时获得无用的System.InvalidOperationException
。
这里是文档的完整示例的一部分
public class Group { /* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base type (Employee) and derived type (Manager) into serialized arrays. */ [XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))] public Employee[] Employees;
私有variables/属性在XML序列化的默认机制中没有被序列化,而是在二进制序列化中。
标记为Obsolete
属性的属性不会被序列化。 我还没有使用Deprecated
属性进行testing,但是我认为它会以相同的方式进行testing。
我不能解释这一个,但我发现这不会连载:
[XmlElement("item")] public myClass[] item { get { return this.privateList.ToArray(); } }
但是这将会:
[XmlElement("item")] public List<myClass> item { get { return this.privateList; } }
另外值得注意的是,如果你正在序列化到一个memstream,你可能想要在使用它之前find0。
在没有显式序列化的情况下小心序列化types,当.Net构build它们时会导致延迟。 我最近在序列化RSAParameters时发现了这个问题。
如果您的XSD使用replace组,那么您很可能无法(自动)将其序列化。 您将需要编写自己的序列化程序来处理这种情况。
例如。
<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。 它只会序列化和从基类消息类。
私有variables/属性在XML序列化中不是序列化的,而是在二进制序列化中。
我相信如果你通过公共属性暴露私有成员,这也会得到你 – 私有成员不会被序列化,所以公共成员都引用空值。