如何为SomeClass实现ReadXml:IList <ifoo>其中IFoo的所有实例都是不同T的IFoo <t>

时间:2015-07-23 15:39:37

标签: c# xml-serialization ixmlserializable

我有一个我需要序列化/反序列化的类,我在那里 - 我有序列化功能,导致下面的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个实例。根据{{​​1}}方法中使用的方法,我期望该类的使用者在他们的实现中保存足够的数据,这些实现允许将生成的类转换回其原始形式。 。所以,是的,失去了保真度,但这是我可以忍受的,以换取使用接口列表而不是基类列表的灵活性。

2 个答案:

答案 0 :(得分:0)

一个解决方案是回避问题(无论如何,IXmlSerializable.ReadXml看起来非常痛苦,例如对于集合)。我最终做的是废弃IXmlSerializable,而是沿着下面的行生成一个类。

请注意,虽然这种方法有效,但如果序列化实例用于序列化以外的任何其他操作,则目前非常容易出错 - 仅在设置或检索SerializationTarget时才保持同步。设置后,我们将现有参数转换为适当的实例,并将它们添加到可序列化列表中。当它被检索时,如果它为空,我们从当前值中的任何东西膨胀。

但是,如果在创建此对象后FooContainer发生更改,则不会保持该同步,并且序列化的内容将过时。这主要是因为我很懒,并且不想再次实现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;}
    }
}

答案 1 :(得分: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>

反序列化保持完全保真。