Чи можливо дезаріалізацію XML у Список <T>?


155

Враховуючи таку XML:

<?xml version="1.0"?>
<user_list>
   <user>
      <id>1</id>
      <name>Joe</name>
   </user>
   <user>
      <id>2</id>
      <name>John</name>
   </user>
</user_list>

І наступний клас:

public class User {
   [XmlElement("id")]
   public Int32 Id { get; set; }

   [XmlElement("name")]
   public String Name { get; set; }
}

Чи можна використовувати XmlSerializerдесеріалізацію xml в a List<User>? Якщо так, то який тип додаткових атрибутів мені потрібно використовувати або які додаткові параметри мені потрібно використовувати для побудови XmlSerializerекземпляра?

Масив ( User[]) був би прийнятним, якщо трохи менш кращим.

Відповіді:


137

Ви можете трибуально скласти список:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

[XmlRoot("user_list")]
public class UserList
{
    public UserList() {Items = new List<User>();}
    [XmlElement("user")]
    public List<User> Items {get;set;}
}
public class User
{
    [XmlElement("id")]
    public Int32 Id { get; set; }

    [XmlElement("name")]
    public String Name { get; set; }
}

static class Program
{
    static void Main()
    {
        XmlSerializer ser= new XmlSerializer(typeof(UserList));
        UserList list = new UserList();
        list.Items.Add(new User { Id = 1, Name = "abc"});
        list.Items.Add(new User { Id = 2, Name = "def"});
        list.Items.Add(new User { Id = 3, Name = "ghi"});
        ser.Serialize(Console.Out, list);
    }
}

5
Приємне рішення з [XmlElement ("користувачем")], щоб уникнути зайвого рівня елементів. Дивлячись на це, я впевнено подумав, що він видав би <user> або <Items> вузол (якщо у вас не був атрибут XmlElement), а потім додати <user> вузли під цим. Але я спробував це, і це не вийшло, тим самим випромінюючи саме те, що питання хотіло.
— Джон Краг

Що робити, якщо я мав два списки під UserList вище? Я спробував ваш метод, і він говорить, що він вже визначає учасника, який називається XYZ з тими ж параметрами
— Kala J

Я не знаю, чому це позначено як правильну відповідь. Він включає додавання класу для завершення списку. Це було, безумовно, те, чого намагається уникнути.
— DDRider62

1
@ DDRider62 питання не говорить "без обгортання". Більшість людей досить прагматичні і просто хочуть отримати дані. Ця відповідь дозволяє зробити це через .Itemsчлена.
— Марк Гравелл

50

Якщо ви прикрашаєте Userклас XmlTypeвідповідно до необхідної великої літери:

[XmlType("user")]
public class User
{
   ...
}

Тоді XmlRootAttributeна XmlSerializerctor можна надати потрібний корінь і дозволити безпосереднє зчитування в Список <>:

    // e.g. my test to create a file
    using (var writer = new FileStream("users.xml", FileMode.Create))
    {
        XmlSerializer ser = new XmlSerializer(typeof(List<User>),  
            new XmlRootAttribute("user_list"));
        List<User> list = new List<User>();
        list.Add(new User { Id = 1, Name = "Joe" });
        list.Add(new User { Id = 2, Name = "John" });
        list.Add(new User { Id = 3, Name = "June" });
        ser.Serialize(writer, list);
    }

...

    // read file
    List<User> users;
    using (var reader = new StreamReader("users.xml"))
    {
        XmlSerializer deserializer = new XmlSerializer(typeof(List<User>),  
            new XmlRootAttribute("user_list"));
        users = (List<User>)deserializer.Deserialize(reader);
    }

Кредит: на основі відповіді від YK1 .


11
На мою точку зору, це однозначно відповідь на питання. Питання стосувалося десеріалізації в Список <T>. Усі інші рішення, крім одного, включають клас обгортання, щоб містити список, що, звичайно, не було розміщеним питанням, і чого, схоже, намагається уникати автор запитання.
— DDRider62

1
При такому підході XmlSerializerнеобхідно статично кешувати та використовувати повторно, щоб уникнути сильного витоку пам’яті, див. Витік пам’яті за допомогою StreamReader та XmlSerializer для деталей.
— dbc

16

Так, Список буде серіалізувати та десеріалізувати Список <>. Просто переконайтеся, що ви використовуєте атрибут [XmlArray], якщо є сумніви.

[Serializable]
public class A
{
    [XmlArray]
    public List<string> strings;
}

Це працює як з Serialize (), так і з Deserialize ().


16

Я думаю, що я знайшов кращий шлях. Не потрібно ставити атрибути у свої класи. Я створив два методи серіалізації та десеріалізації, які беруть загальний список як параметр.

Погляньте (це працює на мене):

private void SerializeParams<T>(XDocument doc, List<T> paramList)
    {
        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(paramList.GetType());

        System.Xml.XmlWriter writer = doc.CreateWriter();

        serializer.Serialize(writer, paramList);

        writer.Close();           
    }

private List<T> DeserializeParams<T>(XDocument doc)
    {
        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(List<T>));

        System.Xml.XmlReader reader = doc.CreateReader();

        List<T> result = (List<T>)serializer.Deserialize(reader);
        reader.Close();

        return result;
    }

Таким чином, ви можете серіалізувати будь-який список, який хочете! Не потрібно вказувати тип списку кожен раз.

        List<AssemblyBO> list = new List<AssemblyBO>();
        list.Add(new AssemblyBO());
        list.Add(new AssemblyBO() { DisplayName = "Try", Identifier = "243242" });
        XDocument doc = new XDocument();
        SerializeParams<T>(doc, list);
        List<AssemblyBO> newList = DeserializeParams<AssemblyBO>(doc);

3
Дякуємо, що реально відповіли на питання. Я додам, що для List<MyClass>елемента документа слід вказати ім’я ArrayOfMyClass.
— Макс Торо

8

Так, це дезаріалізується в Список <>. Не потрібно зберігати його в масиві та загортати / інкапсулювати в список.

public class UserHolder
{
    private List<User> users = null;

    public UserHolder()
    {
    }

    [XmlElement("user")]
    public List<User> Users
    {
        get { return users; }
        set { users = value; }
    }
}

Десеріалізація коду,

XmlSerializer xs = new XmlSerializer(typeof(UserHolder));
UserHolder uh = (UserHolder)xs.Deserialize(new StringReader(str));

5

Не впевнений у списку <T>, але масиви, безумовно, вдається. І трохи магії робить насправді легко знову потрапити до Списку.

public class UserHolder {
   [XmlElement("list")]
   public User[] Users { get; set; }

   [XmlIgnore]
   public List<User> UserList { get { return new List<User>(Users); } }
}

2
Чи можна обійтися без класу "власник"?
— Даніель Шаффер

@Daniel, AFAIK, вип. Потрібно серіалізувати та десеріалізувати у конкретний тип об'єкта. Я не вірю, що серіалізація XML споконвічно підтримує класи колекції як початок серіалізації. Я не знаю цього на 100%.
— JaredPar

[XmlElement ("список") замість цього повинен бути [XmlArray ("список")]. Це єдиний спосіб, яким мені допомогла десеріалізація в .NET 4.5
— eduardobr

2

Як щодо

XmlSerializer xs = new XmlSerializer(typeof(user[]));
using (Stream ins = File.Open(@"c:\some.xml", FileMode.Open))
foreach (user o in (user[])xs.Deserialize(ins))
   userList.Add(o);    

Не особливо фантазії, але це має працювати.


2
Ласкаво просимо в stackoverflow! Завжди краще надати короткий опис зразкового коду для підвищення точності публікації :)
— Програмне забезпечення Picrofo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.