Есть ли способ сохранить атрибуты XML при сериализации части класса?
Я пытаюсь сериализовать только часть класса. Я добавил атрибуты XML к членам класса, чтобы сгенерированные теги XML были правильно названы в соответствии со спецификацией, независимо от того, как названы мои свойства. Это прекрасно работает при сериализации основного класса. Однако, если я просто хочу сериализовать часть класса, я теряю атрибуты XML, и имена возвращаются к своим значениям по умолчанию. Есть ли способ сохранить атрибуты XML при сериализации только части класса?
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
Когда я сериализую весь класс, я получаю это (что именно так, как я и ожидал):
<someConfiguration>
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
</someConfiguration>
Если я пытаюсь просто сериализовать часть "Ошибки" класса, я получаю это (обратите внимание, что атрибуты XML, которые изменяют имена тегов, все игнорируются):
<ArrayOfString>
<string>Bug1</string>
<string>Bug2</string>
<string>Bug3</string>
</ArrayOfString>
Мне нужно получить это:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
Как получить частичный класс для сериализации с вышеуказанными тегами?
Или еще лучше, есть ли способ указать имена тегов при сериализации простого List<object>
, Так что вы можете указать тег, используемый для списка, а не с помощью <ArrayOfobject>
и укажите тег, используемый для элементов массива вместо <object>
?
3 ответа
Есть ли способ указать имена тегов при сериализации простого списка.
В общем, в зависимости от точного сценария, это может быть возможно заставить это работать. См. Как MSDN : Указать альтернативное имя элемента для потока XML. Этот пример включает в себя переопределение сериализации определенного поля, но может быть возможно использовать ту же технику для переопределения целых имен типов.
Но мне кажется, что это очень неприятно. Вместо этого, почему бы просто не обработать сериализацию явно:
private static string SerializeByLinqAndToString<T>(
List<T> data, string rootName, string elementName)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(s => new XElement(elementName, s))));
return SaveXmlToString(document);
}
private static string SaveXmlToString(XDocument document)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(sb,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
document.Save(xmlWriter);
}
return sb.ToString();
}
Звоните так:
SomeConfiguration config = ...; // initialize as desired
string result = SerializeByLinq(config.Bugs, "bug", "bugs");
Вышеприведенное работает только со списком строк или списком типов, где содержимое элемента может быть просто результатом вызова ToString()
на экземпляре типа.
Использование полноценных функций сериализации в.NET может быть целесообразно при работе со сложными типами, но если у вас есть только простой список строк, функция LINQ-to-XML очень удобна.
Если у вас есть более сложные типы, вы можете преобразовать каждый элемент списка в XElement
для DOM и сериализовать что:
private static string SerializeByLinq<T>(
List<T> data, string rootName, string elementName = null)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(t =>
ElementFromText(SerializeObject(t), elementName)
)));
return SaveXmlToString(document);
}
private static XElement ElementFromText(string xml, string name = null)
{
StringReader reader = new StringReader(xml);
XElement result = XElement.Load(reader);
if (!string.IsNullOrEmpty(name))
{
result.Name = name;
}
return result;
}
private static string SerializeObject<T>(T o)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter textWriter = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(textWriter,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
xmlSerializer.Serialize(writer, o,
new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty}));
}
return textWriter.ToString();
}
Во втором примере вы можете опустить имя элемента для дочернего элемента, и он будет просто использовать любой тип, который уже настроен для использования (например, имя типа или любой другой [XmlRoot]
установлено в).
Просто добавив это, вы можете обернуть List<> внутри пользовательского класса:
[XmlRoot("config")]
public class SomeConfiguration
{
[XmlElement("bugs")]
public BugList Bugs { get; set; }
[XmlElement("trees")]
public TreeList Trees { get; set; }
}
[XmlRoot("bugs")]
public class BugList
{
[XmlElement("bug")]
public List<string> Items = new List<string>();
}
[XmlRoot("trees")]
public class TreeList
{
[XmlElement("tree")]
public List<string> Items = new List<string>();
}
Теперь это позволит вам сериализовать отдельные списки, и они будут внедрены, как вы ожидаете.
void Main()
{
var config = new SomeConfiguration
{
Bugs = new BugList { Items = { "Bug1", "Bug2" } },
Trees = new TreeList { Items = { "Tree1", "Tree2" } }
};
// Your config will work as normal.
Debug.WriteLine(ToXml(config)); // <config> <bugs>.. <trees>..</config>
// Your collections are now root-ed properly.
Debug.WriteLine(ToXml(config.Bugs)); // <bugs><bug>Bug1</bug><bug>Bug2</bug></bugs>
Debug.WriteLine(ToXml(config.Trees)); // <trees><tree>Tree1</tree><tree>Tree2</tree></trees>
}
public string ToXml<T>(T obj)
{
var ser = new XmlSerializer(typeof(T));
var emptyNs = new XmlSerializerNamespaces();
emptyNs.Add("","");
using (var stream = new MemoryStream())
{
ser.Serialize(stream, obj, emptyNs);
return Encoding.ASCII.GetString(stream.ToArray());
}
}
Нашел "обходной" способ сделать это.. Вместо того, чтобы помещать атрибуты XMLArray и XMLArrayList над списком<>:
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
Поместите атрибут XmlElement в список, который будет определять тег, который будет использоваться для каждого элемента, и не будет иметь тега, обертывающего список. Ваш тег класса в действительности сделает это за вас.
[XmlRoot ("bugs")]
public class SomeConfiguration
{
[XmlElement("bug")]
public List<string> Bugs { get; set; }
}
Когда вы сериализуете вышесказанное, вы получите:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>