C# .NET XML 序列化问题?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/67959/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 11:04:48  来源:igfitidea点击:

.NET XML serialization gotchas?

提问by Kalid

I've run into a few gotchas when doing C# XML serialization that I thought I'd share:

我在进行 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();
        }
    }
}

Any other XML Serialization gotchas out there?

还有其他 XML 序列化问题吗?

回答by Charles Graham

Private variables/properties are not serialized in the default mechanism for XML serialization, but are in binary serialization.

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

回答by Eric Z Beard

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 序列化代码是生成并放置在单独的 DLL 中的,因此当您的代码中存在破坏序列化程序的错误时,您不会得到任何有意义的错误。就像“无法找到 s3d3fsdf.dll”之类的东西。好的。

回答by Dr8k

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

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

I believe this also gets you if you are exposing the private members through public properties - the private members don't get serialised so the public members are all referencing null values.

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

回答by Charles Graham

I can't make comments yet, so I will comment on Dr8k's post and make another observation. Private variables that are exposed as public getter/setter properties, and do get serialized/deserialized as such through those properties. We did it at my old job al the time.

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

One thing to note though is that if you have any logic in those properties, the logic is run, so sometimes, the order of serialization actually matters. The members are implicitlyordered by how they are ordered in the code, but there are no guarantees, especially when you are inheriting another object. Explicitly ordering them is a pain in the rear.

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

I've been burnt by this in the past.

过去我被这件事烧焦了。

回答by Charles Graham

IEnumerables<T>that are generated via yield returns are not serializable. This is because the compiler generates a separate class to implement yield return and that class is not marked as serializable.

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

回答by user7116

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:

如果您的 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)

The cause of this for me was a plugin loaded using LoadFrom contextwhich has many disadvantages to using the Load context. Quite a bit of fun tracking that one down.

对我来说,这是一个使用LoadFrom 上下文加载的插件,它比使用 Load 上下文有很多缺点。追踪那个人很有趣。

回答by Kalid

Another huge gotcha: when outputting XML through a web page (ASP.NET), you don't want to include the Unicode Byte-Order Mark. Of course, the ways to use or not use the BOM are almost the same:

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

BAD (includes BOM):

坏(包括 BOM):

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

GOOD:

好的:

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

You can explicitly pass false to indicate you don't want the BOM. Notice the clear, obvious difference between Encoding.UTF8and UTF8Encoding.

您可以明确传递 false 以指示您不想要 BOM。请注意Encoding.UTF8和之间清晰、明显的区别UTF8Encoding

The three extra BOM Bytes at the beginning are (0xEFBBBF) or (239 187 191).

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

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

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

回答by annakata

I can't really explain this one, but I found this won't serialise:

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

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

but this will:

但这将:

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

And also worth noting that if you're serialising to a memstream, you might want to seek to 0 before you use it.

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

回答by ilitirit

If your XSD makes use of substitution groups, then chances are you can't (de)serialize it automatically. You'll need to write your own serializers to handle this scenario.

如果您的 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>

In this example, an Envelope can contain Messages. However, the .NET's default serializer doesn't distinguish between Message, ExampleMessageA and ExampleMessageB. It will only serialize to and from the base Message class.

在此示例中,信封可以包含消息。但是,.NET 的默认序列化程序不区分 Message、ExampleMessageA 和 ExampleMessageB。它只会在基本 Message 类之间进行序列化。

回答by Keith

Be careful serialising types without explicit serialisation, it can result in delays while .Net builds them. I discovered this recently while serialising RSAParameters.

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