121

在进行 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 序列化问题吗?

4

19 回答 19

27

另一个大问题:当通过网页 (ASP.NET) 输出 XML 时,您不想包含Unicode Byte-Order Mark。当然,使用或不使用 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/

于 2008-09-18T21:52:50.893 回答
22

我还不能发表评论,所以我将对 Dr8k 的帖子发表评论并进行另一次观察。公开为公共 getter/setter 属性的私有变量,并通过这些属性进行序列化/反序列化。我们一直在我以前的工作中这样做。

不过要注意的一件事是,如果您在这些属性中有任何逻辑,那么逻辑就会运行,所以有时,序列化的顺序实际上很重要。成员按它们在代码中的顺序隐式排序,但不能保证,尤其是当您继承另一个对象时。明确订购它们是一种痛苦。

过去我被这件事烧死了。

于 2008-09-16T00:25:16.840 回答
15

从内存流序列化为 XML 字符串时,请务必使用 MemoryStream#ToArray() 而不是 MemoryStream#GetBuffer() 否则您将得到无法正确反序列化的垃圾字符(因为分配了额外的缓冲区)。

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

于 2009-01-05T21:50:57.847 回答
10

如果序列化程序遇到具有接口作为其类型的成员/属性,它将不会序列化。例如,以下内容不会序列化为 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; }
}
于 2009-08-02T12:00:36.770 回答
9

IEnumerables<T>通过 yield return 生成的是不可序列化的。这是因为编译器生成了一个单独的类来实现 yield return,并且该类没有被标记为可序列化。

于 2008-09-16T01:16:08.013 回答
8

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

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

于 2008-10-29T23:21:39.387 回答
7

哦,这是一个很好的选择:由于 XML 序列化代码是生成并放置在单独的 DLL 中的,因此当您的代码中出现破坏序列化程序的错误时,您不会收到任何有意义的错误。就像“无法找到 s3d3fsdf.dll”之类的东西。好的。

于 2008-09-15T23:46:11.770 回答
6

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

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

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

我从来没有弄清楚为什么,我只是将 Value 更改为 internal ...

于 2009-02-16T08:26:20.887 回答
5

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

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

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

于 2008-12-24T17:02:51.393 回答
4

If you try to serialize an array, List<T>, or IEnumerable<T> which contains instances of subclasses of T, you need to use the XmlArrayItemAttribute to list all the subtypes being used. Otherwise you will get an unhelpful System.InvalidOperationException at runtime when you serialize.

Here is part of a full example from the documentation

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;
于 2012-03-18T17:24:00.547 回答
4

如果您的 XML 序列化生成的程序集与尝试使用它的代码不在同一个加载上下文中,您将遇到可怕的错误,例如:

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 上下文相比有很多缺点。跟踪那个很有趣。

于 2008-09-16T03:05:23.493 回答
4

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

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

http://www.codeproject.com/KB/XML/xmlsettings.aspx

http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx

于 2008-12-19T10:09:16.930 回答
4

请参阅“高级 XML 模式定义语言属性绑定支持”以了解有关 XML 序列化程序支持的内容的详细信息,以及有关支持的 XSD 功能的方式的详细信息。

于 2011-06-15T14:03:07.837 回答
3

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

于 2008-09-15T23:42:50.327 回答
3

标有该Obsolete属性的属性未序列化。我没有测试过Deprecated属性,但我认为它会以相同的方式运行。

于 2011-08-29T21:18:27.670 回答
2

如果您的 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 类之间进行序列化。

于 2008-09-26T11:22:23.877 回答
2

在没有显式序列化的情况下小心序列化类型,它可能会在 .Net 构建它们时导致延迟。我最近在序列化 RSAParameters 时发现了这一点。

于 2008-09-26T11:28:03.300 回答
2

我无法真正解释这一点,但我发现这不会连载:

[XmlElement("item")]
public myClass[] item
{
    get { return this.privateList.ToArray(); }
}

但这将:

[XmlElement("item")]
public List<myClass> item
{
    get { return this.privateList; }
}

还值得注意的是,如果您要序列化到 memstream,您可能希望在使用它之前寻求 0。

于 2008-09-26T10:55:54.453 回答
0

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

如果您通过公共属性公开私有成员,我相信这也会让您受益 - 私有成员不会被序列化,因此公共成员都引用空值。

于 2008-09-15T23:55:28.627 回答