Как вернуться к сериализации XML по умолчанию при реализации IXmlSerializable в базовом классе?
Я пытаюсь сериализовать класс, который наследуется от базового класса, который реализует IXmlSerializable.
Базовый класс, называемый PropertyBag, является классом, который допускает динамические свойства ( кредиты Марку Гравеллу).
Я реализовал IXmlSerializable, чтобы динамические свойства (хранящиеся в словаре) записывались как обычные элементы xml.
Например, при сериализации класса Person с открытым свойством (не динамическим) Name и динамическим свойством Age, я хотел бы, чтобы он генерировал следующий XML:
<Person>
<Name>Tim</Name>
<DynamicProperties>
<Country>
<string>USA</string>
</Country>
</DynamicProperties>
<Person>
Я могу заставить часть работать со следующей реализацией WriteXml в базовом классе PropertyBag:
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 он больше не сериализует обычные (не динамические) свойства, если я не перезаписываю метод WriteXml в Person (что я не хочу делать). Есть ли способ, чтобы в базовом классе я мог автоматически добавлять статические свойства? Я знаю, что могу сделать это вручную, используя рефлексию, но мне было интересно, есть ли какая-то встроенная функциональность в.Net Framework?
2 ответа
Марк, ваш ответ о помещении 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, вы можете найти это здесь.
Я переместил атрибут TypeDescriptionProvider из класса PropertyBag в класс PropertyBagWrapper.
Класс PropertyBag по-прежнему имеет ту же реализацию для метода WriteXml(), что и в вопросе.
Я провел довольно много времени с 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; }
}