. NET XML 序列化陷阱?

在进行 C # XML 序列化时,我遇到了一些问题 我觉得我应该分享一下:


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 序列化陷阱吗?

67548 次浏览

私有变量/属性不在 XML 序列化的默认机制中序列化,而是在二进制序列化中。

Oh here's a good one: since the XML serialization code is generated and placed in a separate DLL, you don't get any meaningful error when there is a mistake in your code that breaks the serializer. Just something like "unable to locate s3d3fsdf.dll". Nice.

私有变量/属性不是 在 XML 序列化中序列化,但 都是二进制序列化的。

我相信如果你通过公共属性暴露私有成员,这也会得到你-私有成员不会被序列化,所以公共成员都引用空值。

我现在还不能发表评论,所以我会在 Dr8k 的帖子上发表评论,然后再做一个观察。作为公共 getter/setter 属性公开的私有变量,并通过这些属性进行序列化/反序列化。我以前的工作就是这样。

但是需要注意的一点是,如果在这些属性中有任何逻辑,就会运行这些逻辑,因此有时序列化的顺序实际上很重要。这些成员按照它们在代码中的排序方式排序为 implicitly,但是没有保证,特别是在继承另一个对象时。明确地订购它们是一件令人头疼的事情。

I've been burnt by this in the past.

通过收益率返回生成的 IEnumerables<T>不可序列化。这是因为编译器生成了一个单独的类来实现产量返回,而该类没有被标记为可序列化的。

If your XML Serialization generated assembly is not in the same Load context as the code attempting to use it, you will run into awesome errors like:

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 context加载的插件,使用 Load 上下文有很多缺点。找到这个还挺有意思的。

另一个巨大的陷阱: 当通过网页(ASP.NET)输出 XML 时,你不想包含 Unicode Byte-Order Mark。当然,使用或不使用 BOM 的方式几乎是一样的:

不良资产(包括 BOM) :

XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);

GOOD:

XmlTextWriter  wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))

您可以显式地传递 false 来表示您不需要 BOM。注意 Encoding.UTF8UTF8Encoding之间明显的区别。

开头的三个额外 BOM 字节是(0xEFBBBF)或(239187191)。

参考资料: 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; }
}

同样值得注意的是,如果您正在序列化一个 memstream,那么在使用它之前可能需要寻找0。

如果您的 XSD 使用了替换组,那么您很可能无法(反)自动序列化它。您需要编写自己的序列化程序来处理这种情况。

Eg.

<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 类之间的关系。

Be careful serialising types without explicit serialisation, it can result in delays while .Net builds them. I discovered this recently 同时序列化 RSA 参数.

不能序列化只读属性。您必须有一个 getter 和一个 setter,即使您从未打算使用反序列化将 XML 转换为对象。

出于同样的原因,不能序列化返回接口的属性: 反序列化器不知道要实例化哪个具体类。

在序列化 Color 和/或 Font 类型的对象时可能会遇到问题。

以下是一些对我有帮助的建议:

Http://www.codeproject.com/kb/xml/xmlsettings.aspx

Http://www.codeproject.com/kb/cs/genericxmlserializition.aspx

还有一点需要注意: 如果使用“默认”XML 序列化,则不能序列化私有/受保护的类成员。

但是您可以在类中指定实现 IXmlSerializer 的自定义 XML 序列化逻辑,并序列化您需要/想要的任何私有字段。

http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

当从内存流序列化成 XML 字符串时,一定要使用 MemoryStream # ToArray ()而不是 MemoryStream # GetBuffer () ,否则最终会产生无法正确反序列化的垃圾字符(因为分配了额外的缓冲区)。

Http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(vs.80).aspx

不能序列化没有无参数构造函数的对象(只是被那个构造函数咬了一口)。

由于某种原因,从以下属性中,Value 被序列化,而不是 FullName:

    public string FullName { get; set; }
public double Value { get; set; }

I never got round to working out why, I just changed Value to internal...

如果序列化程序遇到具有接口作为其类型的成员/属性,它将不进行序列化。例如,以下内容不会序列化为 XML:

public class ValuePair
{
public ICompareable Value1 { get; set; }
public ICompareable Value2 { get; set; }
}

Though this will serialize:

public class ValuePair
{
public object Value1 { get; set; }
public object Value2 { get; set; }
}

有关 XMLSerializer 支持的内容的详细信息,以及支持 XSD 特性的方式的详细信息,请参阅“ 高级 XML 架构定义语言属性绑定支持”。

标记为 Obsolete属性的属性不会序列化。我还没有用 Deprecated属性测试过,但是我假设它会以同样的方式工作。

如果试图序列化包含 Tsubclasses实例的数组、 List<T>IEnumerable<T>,则需要使用 XmlArrayItemAttribute列出所有正在使用的子类型。否则,在运行时序列化时将得到一个无用的 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;