Как сериализовать объект в XML, выравнивая внутреннюю иерархию свойств внутри

#c# #.net #xml #serialization

#c# #.net #xml #сериализация

Вопрос:

Я сериализую объект с помощью XmlSerializer, указывая атрибуты внутри моего объекта, чтобы давать инструкции XmlSerializer, например:

 [XmlType("FooElement")]
public class Foo {

    [XmlAttribute("Type")]
    public string fooType;

    [XmlElement("Name")]
    public string Name;

    [XmlElement("Bar")]
    public Bar BarObject;
}

public class Bar {
    [XmlElement("Message")]
    public string BarString;
    [XmlElement("From")]
    public string FromName;
}
 

Когда объект типа Foo сериализуется, генерируется следующий XML:

 <Foo Type="TypeName">
  <Name>Name of Foo</Name>
  <Bar>
     <Message>5:00 Somewhere</Message>
     <From>Jane Smith</From>
  </Bar>
</Foo>
 

Я хочу, чтобы все данные в Foo были выровнены до одного уровня, например:

 <Foo Type="TypeName">
  <Name>Name of Foo</Name>
  <Message>5:00 Somewhere</Message>
  <From>Jane Smith</From>
</Foo>
 

Для моих нужд важно, чтобы Foo и Bar оставались отдельными классами. Помимо объединения классов, как бы я мог объединить элементы Foo в один уровень при сериализации в формате XML? Было бы лучше, если бы это можно было сделать с помощью тегов [Property] . Я открыт для переключения моего метода сериализации XML.

Редактировать: я переключился на класс XmlWriter. Это позволит мне писать XML-элементы в иерархии, которую я ищу. Программа, использующая созданный здесь XML, намного проще, чем инструменты C # XML. У более простого анализатора возникли трудности с XML, сгенерированным XmlSerializer.

Ответ №1:

Это должно сделать это

 void Main()
{
    var foo = new Foo
    {
        Name = "name",
        fooType = "type",
        BarObject = new Bar { BarString = "bar"}
    };

    var s = new XmlSerializer(typeof(Foo));
    using (var writer = new StringWriter())
    {
        s.Serialize(writer, foo);
        writer.ToString().Dump();
    }
}


public class Foo {

    [XmlAttribute("Type")]
    public string fooType;

    [XmlElement("Name")]
    public string Name;

    [XmlElement("Message")]
    public Bar BarObject;
}

public class Bar {

    [XmlText]
    public string BarString;
}
 

Вывод:

 <?xml version="1.0" encoding="utf-16"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Type="type">
  <Name>name</Name>
  <Message>bar</Message>
</Foo>
 

Комментарии:

1. Это выглядит великолепно. Один вопрос: можно ли масштабировать это так, чтобы, если Bar содержит более одного элемента (сообщение, дату и т. Д.), Каждый элемент в Bar отображался в элементе Foo в XML?

2. @ford, если вы добавите дополнительные свойства / поля в Bar класс? Конечно, почему бы и нет. Просто добавьте и пометьте их как [XmlElement] . В противном случае я не уверен, что вы имеете в виду.

3. Спасибо за просьбу о разъяснении. Я отредактировал свой вопрос.

4. @ford я вижу … нет, насколько мне известно, это невозможно. Я спрашиваю, зачем вам это нужно, поскольку сериализованные XML-данные предназначены для представления дерева объектов, которое вы сериализуете. Даже приведенный мной пример является взломом, поскольку XmlText сериализуется как узел (текстовый узел), который является дочерним элементом узла Message . Возможно, если бы вы представили свою проблему более высокого уровня, я мог бы придумать обходной путь или убедить вас, что ваше требование недействительно.

5. знать это достаточно хорошо. Я рассмотрю это под другим углом и устраню требование сглаживания иерархии. В основном я хотел упростить XML для читателя с другой стороны, и похоже, что этот метод вместо этого усложнит ситуацию.

Ответ №2:

Я не знаю способа сделать это без добавления свойств в Foo, которые сопоставляются с соответствующими свойствами Bar . Например:

 [XmlType("FooElement")]
public class Foo {

    [XmlAttribute("Type")]
    public string fooType;

    [XmlElement("Name")]
    public string Name;

    [XmlIgnore()]
    public Bar BarObject;

    [XmlElement("Message")]
    public string BarMessage
    {
       get {
          return this.Bar.BarString;
       }
       set {
          this.Bar.BarString = value;
       }
}

public class Bar {
    public string BarString;
}