关于C#:. NET XML序列化陷阱?

关于C#:. NET XML序列化陷阱?

.NET XML serialization gotchas?

在执行C#XML序列化时遇到了一些陷阱
我以为我会分享:

  • 您无法序列化只读项(例如KeyValuePairs)
  • 您无法序列化通用词典。 相反,请尝试以下包装器类(来自http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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的方式几乎相同:

不良(包括BOM):

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

好:

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

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

开头的三个额外的BOM字节为(0xEFBBBF)或(239 187 191)。

参考:http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/


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

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

过去我一直为此感到疲倦。


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

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


如果序列化程序遇到类型为接口的成员/属性,则不会序列化。例如,以下代码不会序列化为XML:

1
2
3
4
5
public class ValuePair
{
    public ICompareable Value1 { get; set; }
    public ICompareable Value2 { get; set; }
}

虽然这将序列化:

1
2
3
4
5
public class ValuePair
{
    public object Value1 { get; set; }
    public object Value2 { get; set; }
}

通过yield return生成的IEnumerables< T >不可序列化。这是因为编译器会生成一个单独的类来实现收益回报,并且该类未标记为可序列化。


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

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


哦,这是一个很好的例子:由于XML序列化代码已生成并放置在单独的DLL中,因此当代码中出现错误而使序列化程序中断时,您不会遇到任何有意义的错误。就像"无法找到s3d3fsdf.dll"之类的东西。真好


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

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

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

我从来没有想过为什么,我只是将Value更改为Internal ...


需要注意的另一件事:如果使用"默认" XML序列化,则不能序列化私有/受保护的类成员。

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

http://msdn.microsoft.com/zh-CN/library/system.xml.serialization.ixmlserializable.aspx


有关XML序列化程序支持的内容的详细信息以及支持受支持的XSD功能的方式的详细信息,请参见"高级XML架构定义语言属性绑定支持"。


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

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

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

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


如果尝试序列化包含T子类实例的数组,List< T >IEnumerable< T >,则需要使用XmlArrayItemAttribute列出所有正在使用的子类型。否则,您在序列化时会在运行时得到无用的System.InvalidOperationException

这是文档中完整示例的一部分

1
2
3
4
5
6
7
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;

如果您的XML序列化生成的程序集与尝试使用它的代码不在同一Load上下文中,则会遇到诸如以下的错误:

1
2
3
4
5
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上下文相比有许多缺点。跟踪下来很有趣。


标有Obsolete属性的属性不会序列化。我尚未使用Deprecated属性进行测试,但我认为它的作用方式相同。


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


我不能真正解释这一点,但是我发现这不会序列化:

1
2
3
4
5
[XmlElement("item")]
public myClass[] item
{
    get { return this.privateList.ToArray(); }
}

但这将:

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

同样值得注意的是,如果要序列化到内存流,则可能需要先使用0。


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


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

例如。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<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类之间进行序列化。


Private variables/properties are not
serialized in XML serialization, but
are in binary serialization.

如果您通过公共属性公开私有成员,我相信这也会为您带来好处-私有成员不会被序列化,因此公共成员都引用了空值。


推荐阅读

    linux执行两次命令?

    linux执行两次命令?,系统,信息,连续,名称,命令,初级,首页,工具,管理,终端,lin

    linux执行c程序命令?

    linux执行c程序命令?,系统,工作,工具,信息,代码,命令,文件,保险,管理,环境,li

    linux反序列化命令?

    linux反序列化命令?,状态,数据,情况,环境,网络,适当,下来,接口,对象,序列,序

    linux命令行执行成功?

    linux命令行执行成功?,系统,信息,状态,服务,管理,百度,设计,灵活,代码,命令,L

    安卓执行linux命令行?

    安卓执行linux命令行?,系统,设备,基础,发展,标准,情况,信息,电话,号码,工具,

    c执行在linux命令?

    c执行在linux命令?,系统,环境,保险,工具,代码,命令,程序,文件,终端,语言,如何

    linux执行中退出命令?

    linux执行中退出命令?,档案,状态,命令,分析,数据,电脑,实时,系统,工具,编辑,l

    linux授权命令执行?

    linux授权命令执行?,系统,工作,数字,权限,文件,概念,标准,命令,目录,用户,在L

    linux定时执行命令?

    linux定时执行命令?,时间,系统,服务,任务,工作,标准,情况,周期性,工具,命令,l

    如何在linux执行命令?

    如何在linux执行命令?,单位,电脑,命令,系统,基础,发行,工具,工作,信息,文件,m

    linux看命令执行过程?

    linux看命令执行过程?,系统,服务,状态,软件,时间,数据,地址,命令,进程,情况,l

    linux登陆执行命令?

    linux登陆执行命令?,系统,服务,工具,地址,密码,百度,管理,检测,网络,第一,怎

    linux权限命令可执行?

    linux权限命令可执行?,系统,工具,信息,权限,数字,网络,发行,底部,代码,文件,l

    linux远程执行多命令?

    linux远程执行多命令?,工具,服务,命令,状态,暂停,代码,底部,时间,地址,系统,L

    linux下执行命令行?

    linux下执行命令行?,工作,系统,信息,单位,命令,基础,地址,设备,权威,标准,mv

    linux命令行执行工具?

    linux命令行执行工具?,工具,系统,网络,分析,工作,服务,状态,信息,电脑,发行,s

    linux安装执行命令?

    linux安装执行命令?,系统,软件,网络,密码,官方网站,在线,工作,盘中,电脑,第

    linux执行命令超时?

    linux执行命令超时?,时间,代码,系统,名字,环境,工作,服务,下来,名称,地址,如

    linux下执行命令行?

    linux下执行命令行?,工作,系统,信息,单位,命令,基础,地址,设备,权威,标准,mv

    linux远程执行多命令?

    linux远程执行多命令?,工具,服务,命令,状态,暂停,代码,底部,时间,地址,系统,L