我正在尝试序列化一个继承自实现IXmlSerializable的基类的类。
名为PropertyBag的基类是一个允许动态属性(credits to Marc Gravell)的类。
我实现了IXmlSerializable,以便将动态属性(存储在Dictionary中)写为普通的xml元素。
e.g。 当序列化具有公共属性(非动态)名称和动态属性Age的类时,我希望它生成以下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中是否有一些内置功能?
答案 0 :(得分:3)
我花了很多时间使用XmlSerializer
(和其他各种序列化API),我很确定这一点:你做不到。实施IXmlSerializable
全有或全无。
我能想到的最接近的是欺骗并将所有固定属性移动到子对象;这会给你略微不同的xml - 比如:
<FixedProperties>
<Name>Tim</Name>
</FixedProperties>
<DynamicProperties>
<Country>
<string>USA</string>
</Country>
</DynamicProperties>
但我希望它能奏效。您将在基础对象上具有pass-thru属性:
[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; }
}
答案 1 :(得分:1)
Marc,你把FixedProperties放在一个单独的集合中的答案让我想到,我应该创建一个这种类型的属性,而不是继承PropertyBag。
所以我创建了一个我的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; }
}
我不会重复PropertyBag的所有代码以及ICustomTypeDescriptor实现所需的自定义类,您可以找到here。
我确实将TypeDescriptionProvider属性从PropertyBag类移动到了PropertyBagWrapper类。
PropertyBag类仍然具有与问题中发布的WriteXml()方法相同的实现。