121

共有したいと思っていたC#XMLシリアル化を行うときに、いくつかの落とし穴に遭遇しました。

  • 読み取り専用のアイテム(KeyValuePairsなど)をシリアル化することはできません
  • 一般的な辞書をシリアル化することはできません。代わりに、次のラッパークラスを試してください(http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspxから):

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

もう 1 つの大きな落とし穴: Web ページ (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))

BOM が不要であることを示すために、明示的に false を渡すことができます。と の明確で明らかな違いに注意してEncoding.UTF8くださいUTF8Encoding

先頭の 3 つの余分な 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#GetBuffer() の代わりに必ず MemoryStream#ToArray() を使用してください。そうしないと、(余分なバッファーが割り当てられるため) 適切に逆シリアル化されないジャンク文字になってしまいます。

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 リターンを介して生成されたものはシリアライズできません。これは、コンパイラが yield リターンを実装する別のクラスを生成し、そのクラスがシリアライズ可能としてマークされていないためです。

于 2008-09-16T01:16:08.013 に答える
8

読み取り専用プロパティをシリアル化することはできません。XMLをオブジェクトに変換するために逆シリアル化を使用する予定がない場合でも、ゲッターとセッターが必要です。

同じ理由で、インターフェイスを返すプロパティをシリアル化することはできません。デシリアライザーは、インスタンス化する具体的なクラスを認識しません。

于 2008-10-29T23:21:39.387 に答える
7

これは良い例です。XMLシリアル化コードが生成されて別のDLLに配置されるため、コードに誤りがあり、シリアライザーが破損しても、意味のあるエラーは発生しません。「s3d3fsdf.dllが見つかりません」のようなものです。良い。

于 2008-09-15T23:46:11.770 に答える
5

もう 1 つ注意してください。「デフォルト」の XML シリアル化を使用している場合、プライベート/保護されたクラス メンバーをシリアル化することはできません。

ただし、クラスで IXmlSerializable を実装するカスタム XML シリアライゼーション ロジックを指定し、必要な/必要なプライベート フィールドをシリアライズすることができます。

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

于 2008-12-24T17:02:51.393 に答える
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

のサブクラスのインスタンスを含む配列、List<T>またはをシリアル化しようとする場合は、 XmlArrayItemAttributeを使用して、使用されているすべてのサブタイプをリストする必要があります。そうしないと、シリアル化するときに実行時に役に立たなくなります。IEnumerable<T>TSystem.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;
于 2012-03-18T17:24:00.547 に答える
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 シリアライザーでサポートされる内容の詳細、およびサポートされる XSD 機能がサポートされる方法の詳細については、「高度な XML スキーマ定義言語属性バインディング サポート」を参照してください。

于 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 の既定のシリアライザーは、メッセージ、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 シリアル化ではシリアル化されませんが、バイナリ シリアル化ではシリアル化されます。

これは、パブリック プロパティを介してプライベート メンバーを公開している場合にも役立つと思います。プライベート メンバーはシリアル化されないため、パブリック メンバーはすべて null 値を参照しています。

于 2008-09-15T23:55:28.627 に答える