0

我有一个需要序列化/反序列化的类,我已经完成了一半 - 我有序列化功能,导致下面的 XML。但是,由于我IXmlSerializable自己实现,我不确定 的实现ReadXml应该是什么样子,因为它SomeGenericClass<T>是使用基于属性的标记而不是显式实现进行序列化的 ifIXmlSerializable

<?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

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

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

但是,如果 FooContainer 在创建此对象后发生更改,它将不会保持同步,并且被序列化的内容将过时。这主要是因为我很懒,不想IList<SomeGenericClassBase>再次实现来覆盖AddandRemove方法(尽管这将是更健壮的方法)。

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 回答