it-swarm.cn

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

119
Kalid

另一个巨大的问题:当通过网页(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.UTF8UTF8Encoding之间明显的明显区别。

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

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

26
Kalid

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

需要注意的一点是,如果这些属性中有任何逻辑,则运行逻辑,因此有时序列化的顺序实际上很重要。成员是 隐式 按它们在代码中的排序顺序排序,但是没有保证,特别是当你继承另一个对象时。明确地命令他们是后方的痛苦。

我过去一直被这个烧了。

20
Charles Graham

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

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

14
realgt

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

9
bwillard

如果序列化程序遇到具有接口作为其类型的成员/属性,则它将不会序列化。例如,以下内容不会序列化为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; }
}
9
Allon Guralnek

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

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

8
Tim Robinson

哦,这是一个很好的:因为XML序列化代码是生成并放在一个单独的DLL中,所以当代码中的错误打破了序列化程序时,您不会收到任何有意义的错误。就像“无法找到s3d3fsdf.dll”之类的东西。尼斯。

7
Eric Z Beard

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

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

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

我从来没有弄清楚为什么,我只是将价值改为内部......

5
Benjol

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

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

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

5
Max Galkin

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

以下是给我的建议:

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

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

4
Max Galkin

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

4
John Saunders

如果您尝试序列化包含 subslasses T实例的数组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;
4
MarkJ

如果您的XML Serialization生成的Assembly与尝试使用它的代码不在同一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上下文有许多缺点。跟踪那一个很有趣。

4
user7116

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

3
James Hulse

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

3
Charles Graham

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

例如。

<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类进行序列化。

2
ilitirit

小心序列化类型而不进行显式序列化,它可能会导致延迟,而.Net构建它们。我最近发现了这个 同时序列化RSAParameters

2
Keith

我无法解释这一个,但我发现这不会序列化:

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

但这会:

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

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

2
annakata

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

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

0
Dr8k