.NET XML serialization gotchas? [closed]

2020-01-23 13:36发布

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


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?

19条回答
放我归山
2楼-- · 2020-01-23 13:50

You can't serialize read-only properties. You must have a getter and a setter, even if you never intend to use deserialization to turn XML into an object.

For the same reason, you can't serialize properties that return interfaces: the deserializer wouldn't know what concrete class to instantiate.

查看更多
The star\"
3楼-- · 2020-01-23 13:50

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)

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

查看更多
SAY GOODBYE
4楼-- · 2020-01-23 13:50

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.

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.

查看更多
Lonely孤独者°
5楼-- · 2020-01-23 13:51

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.

查看更多
男人必须洒脱
6楼-- · 2020-01-23 13:51

See "Advanced XML Schema Definition Language Attributes Binding Support" for details of what is supported by the XML Serializer, and for details on the way in which the supported XSD features are supported.

查看更多
手持菜刀,她持情操
7楼-- · 2020-01-23 13:59

When serializing into an XML string from a memory stream, be sure to use MemoryStream#ToArray() instead of MemoryStream#GetBuffer() or you will end up with junk characters that won't deserialize properly (because of the extra buffer allocated).

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

查看更多
登录 后发表回答