Сериализация списка, экспортированного как ICollection в XML

у меня есть приложение C# .NET 3.5, где я хотел бы сериализовать класс, содержащий List<> в XML. Мой класс выглядит так:

[XmlRoot("Foo")]
class Foo
{
    private List<Bar> bar_ = new List<Bar>();

    private string something_ = "My String";

    [XmlElement("Something")]
    public string Something { get { return something_; } }

    [XmlElement("Bar")]
    public ICollection<Bar> Bars
    {
        get { return bar_; }
    }
}

если я заполняю его так:

Bar b1 = new Bar();
// populate b1 with interesting data
Bar b2 = new Bar();
// populate b2 with interesting data

Foo f = new Foo();
f.Bars.Add(b1);
f.Bars.Add(b2);

а затем сериализуйте его следующим образом:

using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(@"C:foo.xml"))
{
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
    serializer.Serialize(textWriter, f);
}

Я получаю файл, который выглядит так:

<Foo>
    <Something>My String</Something>
</Foo>

но, что я хочу-это XML, который выглядит так:

<Foo>
    <Something>My String</Something>
    <Bar>
        <!-- Data from first Bar -->
    </Bar>
    <Bar>
        <!-- Data from second Bar -->
    </Bar>
</Foo>

что мне нужно сделать, чтобы получить List<> чтобы появиться в XML?

2 ответов


на XmlSerializer требует, чтобы сериализуемые свойства имели сеттер. Кроме того,XmlSerializer не удается сериализовать свойства интерфейса. Будет работать следующий код:

[XmlElement("Bar")]
public List<Bar> Bars
{
    get { return bar_; }
    set { throw new NotSupportedException("This property 'Bars' cannot be set. This property is readonly."); }
}

Если вам не нравится это решение (исключение является своего рода уродливым), то вы можете реализовать IXmlSerializable и напишите свою собственную сериализацию.

Edit: Артур Мустафин прав, участники, которые реализуют IEnumerable или ICollection не нужен сеттер, как это объяснил на эта страница msdn:

XmlSerializer дает специальное обращение к классам, которые реализуют IEnumerable или ICollection. Класс, реализующий IEnumerable должен реализовать public Add метод, который принимает один параметр. Параметр метода Add должен быть того же типа, что и возвращаемый из Current свойство для значения, возвращаемого из GetEnumerator, или одна из баз такого типа. Класс, реализующий ICollection (например, CollectionBase) в дополнение к IEnumerable должен быть общедоступный Item индексированное свойство (индексатор в C#), которое принимает целое число, и оно должно иметь public Count свойство типа integer. Параметр Add метод должен быть того же типа, что возвращается из Item свойство или одна из баз этого типа. Для классов, реализующих ICollection, значения для сериализации извлекаются из индексированного Item свойство, не вызывая GetEnumerator.


давая правильный ответ, нет смысла создавать уродливый сеттер для List<T> public property, чтобы создать исключение.

это так List<> уже реализует ICollection<T> и предоставляет метод с подписью void Add(T object) который используется механизмом сериализации;

вам нужно только добавить сеттер к сериализуемым общедоступным свойствам и изменить ICollection<T> to List<T>:

[XmlRoot("Foo")]
public class Foo
{
    private List<Bar> bar_ = new List<Bar>();

    [XmlElement("Something")]
    public string Something { get; set; }

    [XmlElement("Bar")]
    public List<Bar> Bars { get { return bar_; } }
}

вы получите вывод:

<?xml version="1.0" encoding="utf-8"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Something>My String</Something>
  <Bar />
  <Bar />
</Foo>

кроме того, лучше сериализовать xml в памяти, чтобы увидеть результаты или проверить его следующим образом:

static void Main(string[] args)
{
     Bar b1 = new Bar();
     // populate b1 with interesting data
     Bar b2 = new Bar();
     // populate b2 with interesting data

     Foo f = new Foo();
     f.Bars.Add(b1);
     f.Bars.Add(b2);
     f.Something = "My String";

     using (MemoryStream ms = new MemoryStream())
     using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(ms))
     {
         System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
         serializer.Serialize(textWriter, f);
         string text = Encoding.UTF8.GetString(ms.ToArray());
         Console.WriteLine(text);
     }

    Console.ReadKey(false);
}

для сериализации с помощью интерфейсов используйте мой проект XmlSerialization