2

XmlSerializerクラスとその関連属性を使用してシリアライズしたいクラスの階層があります。基本抽象クラスがあり、その後にかなりの数の派生クラスがあります (以下のコードでは、派生クラスの数を 5 つに減らしましたが、実際のコードにはさらに多くのクラスがあります)。クラスは階層を形成し、多くの場合、階層内のクラスのインスタンスへの参照が含まれています。

public abstract class BaseType 
{
    // Only classes in my assembly can derive from this class
    internal BaseType() { }   
}

public sealed class TType : BaseType
{
    [XmlText]
    public string Name;
}

public sealed class PType : BaseType
{
    [XmlElement("t", typeof(TType)]
    [XmlElement("p", typeof(PType)]
    [XmlElement("a", typeof(AType)]
    [XmlElement("s", typeof(SType)]
    [XmlElement("u", typeof(UType)]
    public BaseType Child;
}

public sealed class SType : BaseType
{
    [XmlElement("t", typeof(TType)]
    [XmlElement("p", typeof(PType)]
    [XmlElement("s", typeof(SType)]
    [XmlElement("a", typeof(AType)]
    [XmlElement("u", typeof(UType)]
    public BaseType [] Items;
    public string [] ItemNames;
}

public sealed class AType : BaseType
{
    [XmlElement("t", typeof(TType)]
    [XmlElement("p", typeof(PType)]
    [XmlElement("s", typeof(SType)]
    [XmlElement("a", typeof(AType)]
    [XmlElement("u", typeof(UType)]
    public BaseType Item;
    public int Length;
}

public sealed class UType : BaseType
{
    [XmlElement("t", typeof(TType)]
    [XmlElement("p", typeof(PType)]
    [XmlElement("s", typeof(SType)]
    [XmlElement("a", typeof(AType)]
    [XmlElement("u", typeof(UType)]
    public BaseType [] Alts;
    public string [] AltNames;
}

最後に、それらすべてを保持して にフィードするコンテナXmlSerializer:

[XmlRoot("items")]
public class ItemCollection
{
    [XmlElement("t", typeof(TType)]
    [XmlElement("p", typeof(PType)]
    [XmlElement("s", typeof(SType)]
    [XmlElement("a", typeof(AType)]
    [XmlElement("u", typeof(UType)] 
    public BaseType [] Items;
}

ご覧のとおり、私のコードにはかなりの繰り返しがあります。ある時点で、新しい派生クラスが導入される可能性があり、BaseType への参照が使用されているすべての場所に、新しいXmlElement属性を使用して再度アクセスする必要があります。これは退屈で、エラーが発生しやすいです。BaseTypeaは、要素名が「t」の場合は TType として、要素名が「p」の場合は PType として、1 回だけデシリアライズできるという事実を表現したいと思います。

私は知っていますが、「ゴールド所有者」が満足していない属性をXmlIncludeAttribute導入しています. xsi:typeXML 要素名と CLR 型の間のマッピングの知識を除外する方法はありますか?

ソリューションが行うことができる 1 つの仮定は、派生クラスの完全なセットが、 を定義するアセンブリによって認識されているということBaseTypeです。つまり、新しいクラスをミックスに追加する外部アセンブリを考慮する必要はありません。

4

1 に答える 1

2

しばらくこれをいじった後、私は解決策を思いつき、同じ状況で他の人を助けるためにここに投稿しています。

まず、タイプ階層内のすべてのタイプを見つけます。XmlElements次に、これらすべてのタイプを含むインスタンスを構築する関数を記述します。

XmlAttributes CreateXmlAttributesForHierarchyTypes()
{
    return XmlAttributes
    {
        XmlElements = 
        {
            new XmlElementAttribute("t", typeof (TType)),
            new XmlElementAttribute("p", typeof (PType)),
            new XmlElementAttribute("s", typeof (SType)),
            new XmlElementAttribute("a", typeof (AType)),
            new XmlElementAttribute("u", typeof (UType)),
        }
    };
}

次に、シリアル化する上記のタイプのいずれかのフィールドを持つすべてのタイプを見つけます。アセンブリにすべてのクラスを反映することでこれを行うことができますが、私の場合、この要件を持つクラスはごくわずかしかないことがわかりました。

Type [] typesToOverride = new Type[] 
{
    typeof(PType),
    typeof(SType),
    typeof(AType),
    typeof(UType),
    typeof(ItemCollection),
};

次に、適切なタイプの各フィールドのオーバーライドをXmlAttributeOverrides指定するインスタンスを作成します。hierarchyTypeElements

public static XmlAttributeOverrides GetAttributeOverrides(IEnumerable<Type> typesToOverride)
{
    var overrides = typesToOverride
        .SelectMany(x => x.GetFields())  // Get a flat list of fields from all the types
        .Where(f => f.FieldType == typeof (BaseType))  // Must have the right type
        .Select(f => new
        {
            Field = f,
            Attributes = GetXmlAttributes(f)
        })
        .Where(f => f.Attributes != null)
        .Aggregate(
            new XmlAttributeOverrides(),
            (ov, field) =>
            { 
                ov.Add(field.Field.DeclaringType, field.Field.Name, field.Attributes); 
                return ov;
            });
    return overrides;
}

(ええ、私は虐待してAggregateいます; LinQはとても素敵な金のハンマーです)

最後に、XmlAttributeOverridesインスタンスを使用してシリアライザーを作成します。

var attrOverrides = GetAttributeOverrides(TypesToDecorate);
serializer = new XmlSerializer(typeof(ItemCollection), attrOverrides);

アセンブリのリークを回避するために、そのシリアライザーを静的変数にキャッシュすることをお勧めします。

このコードを一般化して、フィールドだけでなくプロパティも装飾することができます。これは、読者の練習問題として残されています。

于 2013-03-22T13:32:51.860 に答える