简体   繁体   English

在基类中实现IXmlSerializable时如何恢复到“默认”XML序列化?

[英]How to revert back to 'default' XML serialization when implementing IXmlSerializable in a base class?

I'm trying to serialize a class that inherits from a base class that implements IXmlSerializable. 我正在尝试序列化一个继承自实现IXmlSerializable的基类的类。

The base class, called PropertyBag is a class that allows dynamic properties ( credits to Marc Gravell ). 名为PropertyBag的基类是一个允许动态属性的类( Marc Gravell的学分 )。

I implemented IXmlSerializable so that the dynamic properties (stored in a Dictionary) are written as normal xml elements. 我实现了IXmlSerializable,以便将动态属性(存储在Dictionary中)写为普通的xml元素。

eg When serializing a class Person with a public property (non dynamic) Name and a dynamic property Age, I would like for it to generate the following XML: 例如,当序列化具有公共属性(非动态)名称和动态属性Age的类时,我希望它生成以下XML:

<Person>
  <Name>Tim</Name>
  <DynamicProperties>
    <Country>
      <string>USA</string>
    </Country>
  </DynamicProperties>
<Person>

I can get the part to work with the following implementation of WriteXml in the base PropertyBag class: 我可以让部分在基础PropertyBag类中使用WriteXml的以下实现:

public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteStartElement("DynamicProperties");

        // serialize every dynamic property and add it to the parent writer
        foreach (KeyValuePair<string, object> kvp in properties)
        {
            writer.WriteStartElement(kvp.Key);

            StringBuilder itemXml = new StringBuilder();
            using (XmlWriter itemWriter = XmlWriter.Create(itemXml))
            {
                // serialize the item
                XmlSerializer xmlSer = new XmlSerializer(kvp.Value.GetType());
                xmlSer.Serialize(itemWriter, kvp.Value);                    

                // read in the serialized xml
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(itemXml.ToString());

                // write to modified content to the parent writer
                writer.WriteRaw(doc.DocumentElement.OuterXml);
            }

            writer.WriteEndElement();
        }

        writer.WriteEndElement();
    }

However, when serializing the Person class, it no longer serializes the normal (non dynamic) properties unless I overwrite the WriteXml method in Person (which I do not want to do). 但是,在序列化Person类时,它不再序列化正常(非动态)属性,除非我在Person中覆盖WriteXml方法(我不想这样做)。 Is there any way that in the base class I can automatically add the static properties? 有没有办法在基类中我可以自动添加静态属性? I know I can do this manually using reflection, but I was wondering if there is some built-in functionality in the .Net Framework? 我知道我可以使用反射手动执行此操作,但我想知道.Net Framework中是否有一些内置功能?

I've spent quite a bit of time with XmlSerializer (and various other serialization APIs), and I'm pretty sure that simply: you can't. 我花了很多时间使用XmlSerializer (以及其他各种序列化API),我很确定这一点:你做不到。 Implementing IXmlSerializable is all or nothing. 实现IXmlSerializable是全有或全无。

The closest I can think of is to cheat and move all the fixed properties to a sub-object; 我能想到的最接近的是欺骗并将所有固定属性移动到子对象; this would give you slightly different xml - something like: 这会给你略微不同的xml - 类似于:

<FixedProperties>
   <Name>Tim</Name>
</FixedProperties> 
<DynamicProperties>
  <Country>
    <string>USA</string>
  </Country>
</DynamicProperties>

but I expect it would work. 但我希望它能奏效。 You would have pass-thru properties on your base object: 您将在基础对象上具有pass-thru属性:

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public FixedProperties FixedProps {get;set;}
public string Name {
    get {return FixedProps.Name;}
    set {FixedProps.Name = value;}
}

Make sense? 说得通? You could also mark Name as [XmlIgnore] , but it seems pretty redundant. 你也可以将Name标记为[XmlIgnore] ,但它看起来很冗余。 In your bespoke serialize method you'd use new XmlSerializer(typeof(FixedProperties)) 在您的定制序列化方法中,您将使用new XmlSerializer(typeof(FixedProperties))

Edit: Here's a working "serialize" example: 编辑:这是一个有效的“序列化”示例:

using System;
using System.ComponentModel;
using System.Xml.Serialization;

static class Program
{
    static void Main()
    {
        MyType obj = new MyType { Name = "Fred" };
        var ser = new XmlSerializer(obj.GetType());
        ser.Serialize(Console.Out, obj);
    }
}
public class MyType : IXmlSerializable
{
    public MyType()
    {
        FixedProperties = new MyTypeFixedProperties();
    }
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public MyTypeFixedProperties FixedProperties { get; set; }
    [XmlIgnore]
    public string Name
    {
        get { return FixedProperties.Name; }
        set { FixedProperties.Name = value; }
    }

    System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
    {
        return null;
    }

    void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
    {
        throw new System.NotImplementedException();
    }

    void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteStartElement("DynamicProperties");
        writer.WriteElementString("Foo", "Bar");
        writer.WriteEndElement();
        fixedPropsSerializer.Serialize(writer, FixedProperties);
    }
    static readonly XmlSerializer fixedPropsSerializer
        = new XmlSerializer(typeof(MyTypeFixedProperties));

}
[XmlRoot("FixedProperties")]
public class MyTypeFixedProperties
{
    public string Name { get; set; }
}

Marc, your answer on putting the FixedProperties in a seperate collection got me thinking that instead of inheriting from PropertyBag, I should create a property of that type. Marc,你把FixedProperties放在一个单独的集合中的答案让我想到,我应该创建一个这种类型的属性,而不是从PropertyBag继承。

So I created a PropertyBagWrapper class that my Person class inherits from and it works. 所以我创建了一个我的Person类继承的PropertyBagWrapper类,它可以工作。

[Serializable]
[TypeDescriptionProvider(typeof(PropertyBagDescriptionProvider))]    
public abstract class PropertyBagWrapper
{
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]    
    public PropertyBag DynamicProperties { get; set; }

    public object this[string name]
    {
        get { return DynamicProperties[name]; }
        set { DynamicProperties[name] = value; }
    }
    protected PropertyBagWrapper()
    {
        DynamicProperties = new PropertyBag(this.GetType());
    }
}

[Serializable]    
public class Person : PropertyBagWrapper
{
    [Browsable(true)]
    public string Name { get; set; }
}

I won't repeat all the code for the PropertyBag and the custom classes needed for ICustomTypeDescriptor implementation, you can find that here . 我不会重复PropertyBag的所有代码和ICustomTypeDescriptor实现所需的自定义类,你可以在这里找到它。

I did move the TypeDescriptionProvider attribute from the PropertyBag class to the PropertyBagWrapper class. 我确实将TypeDescriptionProvider属性从PropertyBag类移动到PropertyBagWrapper类。

The PropertyBag class still has the same implementation for WriteXml() method as posted in the question. PropertyBag类仍然具有与问题中发布的WriteXml()方法相同的实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM