0

シリアル化/逆シリアル化する必要があるクラスがあり、途中まで来ています。シリアル化が機能しているため、以下の XML が生成されます。ただし、私は自分自身を実装しているので、明示的な実装ではなく属性ベースのフラグを使用してシリアル化されていることを考えるとIXmlSerializable、の実装がReadXmlどのように見えるべきかはわかりません。SomeGenericClass<T>IXmlSerializable

<?xml version="1.0" encoding="utf-16"?>
<FooContainer FooName="DoSomething">
  <SomeGenericClassOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Value="Foobar" Name="firstParam" Description="First Paramater Serialized" />
  <SomeGenericClassOfInt32 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Value="10000" Name="nextParam" Description="Second Serialized parameter" />
</FooContainer>

次のインスタンスにシリアライズしたい:

public class FooContainer : IList<ISomeGenericClassBase>, IXmlSerializable
{
     public string FooName {get;set;}

     void IXmlSerializable.WriteXml(XmlWriter writer) {
         var serializer = XmlSerializer.FromTypes(new Type[]{SomeGenericBaseClass})[0];
         this
             .Select(item=>SomeGenericClassBase.ConvertToMe(item))
             .ToList()
             .ForEach(item=>serializer.Serialize(writer, item));
     }

     // IList Implementation omitted - wraps a private List<ISomeGenericClassBase>
}

リストには、次の行に沿ったインスタンスが含まれます。

public interface ISomeGenericClassBase
{
}

public interface ISomeGenericBaseClass<T> : ISomeGenericBaseClass 
{
} 

public class SomeGenericClassBase : ISomeGenericClassBase
{
    public static SomeGenericClassBase ConvertToMe(ISomeGenericClassBase target) {
        return new SomeGenericClassBase() {Property1 = target.Property1; Property2 = target.Property2}
    }

    public static ISomeGenericBaseClass ExpantToTyped(SomeGenericClassBase target) {
        // Implementation omitted - converts a base class instance to a generic instance by working out the generic type from saved data and reconstructing
    }
}

public class SomeGenericClass<T> : SomeGenericClassBase, ISomeGenericBaseClass<T>
{
    [XmlAttribute]
    public string Name {get;set;}

    [XmlAttribute]
    public string Description{get;set;}

    [XmlAttribute]
    public T Value {get;set;}

    [XmlElement("")]
    public T[] ValidOptions {get;set;}

}

編集:実装を拡張しました-そのままでは、問題を正しく説明していませんでした

SomeGenericClassBaseコアの問題は、インスタンスを取得するだけであっても、インターフェイスのみを実装するアイテムをシリアル化できるようにしたいということです。メソッドで使用されているアプローチExpandToTypedに従って、クラスのコンシューマーが実装に十分なデータを保存して、結果のクラスを必要に応じて元の形式に変換できるようにすることを期待しています。確かに、忠実度は失われますが、基本クラスのリストの代わりにインターフェイスのリストを使用するという柔軟性と引き換えに、私はそれを受け入れることができます。

4

2 に答える 2

0

解決策の 1 つは、この問題を回避することです (IXmlSerializable.ReadXml は、コレクションの場合など、いずれにせよかなり面倒に見えます)。私が最終的に行ったことは、IXmlSerializable を破棄し、代わりに以下の行に沿ってクラスを生成することです。

このアプローチは機能しますが、シリアライズ可能なインスタンスがシリアライゼーション以外の目的で使用されている場合、現時点ではエラーが発生しやすいことに注意してください。同期は、SerializationTarget設定または取得された場合にのみ維持されます。設定すると、既存のパラメーターを適切なインスタンスに変換し、シリアル化可能なリストに追加します。取得時に null の場合は、現在の値に含まれていたものから膨張します。

ただし、このオブジェクトの作成後に FooContainer が変更されると、その同期が維持されず、シリアル化されるものが古くなります。これは主に、私が怠け者であり、 andメソッドIList<SomeGenericClassBase>をオーバーライドするために再度実装したくないためです(ただし、これはより堅牢なアプローチです)。AddRemove

public class FooContainerSerializable
{
    public FooContainerSerializable() {}
    public FooContainerSerializable(FooContainer serializationTarget) 
    {
        this.SerializationTarget = serializationTarget;
    }

    [XmlIgnore]
    public FooContainer SerializationTarget
    {
        get {
            if (_SerializationTarget == null)
            {
                _SerializationTarget = new FooContainer();

                // Copy across extant collection properties here
                this.Parameters.ForEach(item=>_SerializationTarget.Add(item));
            }
            return _SerializationTarget;
        }
        set {
            // Synchronize this entity's entries here
            _SerializationTarget = value;
            _SerializationTarget.ForEach(item=>this.Parameters.Add(item.Deflate()));
        }
    }
    private FooContainer _SerializationTarget;

    [XmlElement]
    public string FooName {
        get {return this.SerializationTarget.FooName;}
        set {this.SerializationTarget.FooName = value;}
    }

    [XmlElement]
    public List<SomeGenericClassBase> Parameters {
        get {return _Parameters ?? (_Parameters = new List<SomeGenericClassBase>());}
        set {_Parameters = value;}
    }
}
于 2015-07-24T10:27:48.050 に答える
0

コレクション定義でインターフェイスの代わりに抽象クラスを使用する場合は、別のオプションがあります。また、属性をSomeGenericClassBase使用するすべての派生型を宣言する必要があります。XmlIncludeこのクラスで使用するタイプがほんの一握りであれば、これはそれほど悪くないと思います。

[XmlRoot(ElementName = "FooContainer")]
public class FooContainer : List<SomeGenericClassBase>
{
    [XmlAttribute]
    public string FooName { get; set; }
}    

[XmlInclude(typeof(SomeGenericClass<string>))]
[XmlInclude(typeof(SomeGenericClass<int>))]
public abstract class SomeGenericClassBase
{
    [XmlAttribute]
    public string Name { get; set; }

    [XmlAttribute]
    public string Description { get; set; }
}

public class SomeGenericClass<T> : SomeGenericClassBase
{
    [XmlAttribute]
    public T Value { get; set; }

    [XmlElement]
    public T[] ValidOptions { get; set; }
}

class Class1
{
    public static void Run()
    {
        var f = new FooContainer()
        {
            new SomeGenericClass<string> {  Name = "firstParam", Description = "First Paramater Serialized", Value = "Foobar"},
            new SomeGenericClass<int>  {  Name = "nextParam", Description = "Second Serialized parameter",  Value = 10000 }
        };

        f.FooName = "DoSomething";

        XmlSerializer serializer = new XmlSerializer(f.GetType());            
        StringBuilder sb = new StringBuilder();

        // Serialize
        using (StringWriter writer = new StringWriter(sb))
        {
            serializer.Serialize(writer, f);
        }

        Console.WriteLine(sb);

        // Deserialize
        using(StringReader reader = new StringReader(sb.ToString()))
        {
            FooContainer f2 = (FooContainer)serializer.Deserialize(reader);
        }
    }
}

これは、次の XML にシリアル化されます。

<?xml version="1.0" encoding="utf-16"?>
<FooContainer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SomeGenericClassBase xsi:type="SomeGenericClassOfString" Name="firstParam" Description="First Paramater Serialized" Value="Foobar" />
  <SomeGenericClassBase xsi:type="SomeGenericClassOfInt32" Name="nextParam" Description="Second Serialized parameter" Value="10000" />
</FooContainer>

デシリアライゼーションは、完全な忠実度を維持します。

于 2015-07-24T18:07:08.793 に答える