Вложенные классы десериализованы из плоского XML
Если у меня есть XML-файл, который выглядит как
<Foo>
<Name>Some Data</Name>
<Bar_Data>Other Data</Bar_Data>
<Bar_MoreData>More Data</Bar_MoreData>
</Foo>
И я хочу превратить его в класс C#, который выглядит как
public class Foo
{
public string Name {get; set; }
public Bar Bar { get; set; }
}
public class Bar
{
public string Data { get; set; }
public string MoreData { get; set; }
}
Есть ли способ сделать это с помощью простых аннотаций данных (XmlRoot
, XmlElement
и т. д.) или это мой единственный вариант реализации IXmlSerializable
?
РЕДАКТИРОВАТЬ: Обратите внимание, мне только когда-нибудь нужно десериализовать данные. Я получаю XML из стороннего источника и мне не нужно сериализовать Foo
обратно в XML (если это облегчает).
1 ответ
Одним из вариантов является использование XmlAnyElementAttribute, как показано ниже:
public class Foo
{
public string Name { get; set; }
public Bar Bar { get; set; }
[XmlAnyElementAttribute]
public XmlElement[] BarElements
{
get { return null; }
set
{
Bar = new Bar();
var barType = Bar.GetType();
foreach (var prop in value)
barType.GetProperty(prop.Name.Substring(4)).SetValue(Bar, prop.InnerText);
}
}
}
public class Bar
{
public string Data { get; set; }
public string MoreData { get; set; }
}
Когда XmlSerializer не распознает элемент, он добавляет его к свойству типа XmlElement[], отмеченному XmlAnyElementAttribute. Здесь вы можете обрабатывать свойства Bar. Я использовал отражение там, чтобы показать идею.
Другой вариант - дважды десериализовать и связать Bar с Foo:
public class Foo
{
public string Name { get; set; }
public Bar Bar { get; set; }
}
[XmlRoot("Foo")]
public class Bar
{
[XmlElement("Bar_Data")]
public string Data { get; set; }
[XmlElement("Bar_MoreData")]
public string MoreData { get; set; }
}
var foo = (Foo) new XmlSerializer(typeof (Foo)).Deserialize(...);
var bar = (Bar) new XmlSerializer(typeof (Bar)).Deserialize(...);
foo.Bar = bar
Еще один вариант без вмешательства в десериализацию классов:
public class Foo
{
public string Name { get; set; }
public Bar Bar { get; set; }
}
public class Bar
{
public string Data { get; set; }
public string MoreData { get; set; }
}
var fooSerializer = new XmlSerializer(typeof (Foo));
fooSerializer.UnknownElement += (sender, e) =>
{
var foo = (Foo) e.ObjectBeingDeserialized;
if(foo.Bar == null)
foo.Bar = new Bar();
var propName = e.Element.Name.Substring(4);
typeof(Bar).GetProperty(propName).SetValue(foo.Bar, e.Element.InnerText);
};
var fooInstance = fooSerializer.Deserialize(...);
и если двойная десериализация или рефлексия проблематичны с точки зрения производительности, то вы можете создать суррогатный прокси-класс:
[XmlRoot("Foo")]
public class FooSurrogate
{
public string Name { get; set; }
public string Bar_Data { get; set; }
public string Bar_MoreData { get; set; }
public Foo ToFoo()
{
return new Foo
{
Name = Name,
Bar = new Bar
{
Data = Bar_Data,
MoreData = Bar_MoreData
}
};
}
}
var seializer = new XmlSerializer(typeof (FooSurrogate));
var foo = ((FooSurrogate) seializer.Deserialize(...)).ToFoo();