【问题标题】:How to revert back to 'default' XML serialization when implementing IXmlSerializable in a base class?在基类中实现 IXmlSerializable 时如何恢复为“默认”XML 序列化?
【发布时间】:2009-12-30 22:42:27
【问题描述】:

我正在尝试序列化从实现 IXmlSerializable 的基类继承的类。

名为 PropertyBag 的基类是一个允许动态属性的类 (credits to Marc Gravell)。

我实现了 IXmlSerializable,以便将动态属性(存储在字典中)写为普通的 xml 元素。

例如 当使用公共属性(非动态)名称和动态属性 Age 序列化类 Person 时,我希望它生成以下 XML:

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

我可以让部件与基 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();
    }

但是,当序列化 Person 类时,它不再序列化普通(非动态)属性,除非我覆盖 Person 中的 WriteXml 方法(我不想这样做)。有什么方法可以在基类中自动添加静态属性?我知道我可以使用反射手动执行此操作,但我想知道 .Net Framework 中是否有一些内置功能?

【问题讨论】:

  • 我认为你应该避免使用“静态”这个词,因为它有另一个(非常不同的)含义......

标签: c# ixmlserializable


【解决方案1】:

我已经在XmlSerializer(以及各种其他序列化 API)上花费了相当多的时间,而且我很确定这很简单:你不能。实现IXmlSerializable 要么全有,要么全无。

我能想到的最接近的就是作弊,将所有固定属性移动到一个子对象中;这会给你稍微不同的 xml - 比如:

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

但我希望它会起作用。您将在基础对象上具有传递属性:

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

有意义吗?您也可以将Name 标记为[XmlIgnore],但这似乎是多余的。在您定制的序列化方法中,您将使用 new XmlSerializer(typeof(FixedProperties))

编辑:这是一个有效的“序列化”示例:

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; }
}

【讨论】:

  • 正确的钱。这就是在较新版本的 .Net 中添加 DataContract 的原因。这就是为什么“旧” asmx Web 服务也会失败的原因,因为如果不完全从头开始编写序列化,就无法检测到对象的序列化。
  • 这应该可以,但不符合我的预期目的。派生类已经存在,我只是想为它们添加动态属性的功能。固定属性应保留在派生类中。不过,我非常感谢您的意见!
  • @jvenema - 那么这是否可以使用 DataContract 属性,如果可以,您有什么好的建议吗?
【解决方案2】:

Marc,您关于将 FixedProperties 放在单独的集合中的回答让我想到,我应该创建一个该类型的属性,而不是从 PropertyBag 继承。

所以我创建了一个 PropertyBagWrapper 类,我的 Person 类继承自该类并且它可以工作。

[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; }
}

PropertyBag 和 ICustomTypeDescriptor 实现所需的自定义类的所有代码我就不重复了,你可以找到here

我确实将 TypeDescriptionProvider 属性从 PropertyBag 类移到了 PropertyBagWrapper 类。

PropertyBag 类仍然具有与问题中发布的相同的 WriteXml() 方法实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    相关资源
    最近更新 更多