Чи можливо дезаріалізацію 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.