Có thể giải tuần tự hóa XML thành Danh sách <T> không?


155

Cho XML sau:

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

Và lớp sau:

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

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

Có thể sử dụng XmlSerializerđể khử lưu lượng xml thành a List<User>? Nếu vậy, tôi sẽ cần sử dụng loại thuộc tính bổ sung nào hoặc tôi cần sử dụng tham số bổ sung nào để xây dựng XmlSerializerthể hiện?

Một mảng ( User[]) sẽ được chấp nhận, nếu ít hơn một chút.

Câu trả lời:


137

Bạn có thể gói gọn danh sách một cách tầm thường:

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
Giải pháp tuyệt vời với [XmlEuity ("người dùng")] để tránh mức độ yếu tố thêm. Nhìn vào điều này, tôi nghĩ chắc chắn rằng nó sẽ phát ra nút <user> hoặc <Item> (nếu bạn không có thuộc tính XmlEuity), sau đó thêm các nút <user> vào đó. Nhưng tôi đã thử nó và nó đã không, do đó phát ra chính xác những gì câu hỏi muốn.
Jon Kragh

Nếu tôi có hai danh sách dưới UserList ở trên thì sao? Tôi đã thử phương pháp của bạn và nó nói rằng nó đã xác định một thành viên được gọi là XYZ với cùng loại tham số
Kala J

Tôi không biết tại sao điều này được đánh dấu là câu trả lời đúng. Nó bao gồm thêm một lớp để bọc danh sách. Đó chắc chắn là những gì câu hỏi đang cố gắng tránh.
DDRider62

1
@ DDRider62 câu hỏi không nói "không gói". Hầu hết mọi người đều khá thực dụng và chỉ muốn lấy dữ liệu ra. Câu trả lời này cho phép bạn làm điều đó, thông qua các .Itemsthành viên.
Marc Gravell

50

Nếu bạn trang trí Userlớp với XmlTypeđể phù hợp với viết hoa yêu cầu:

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

Sau đó, XmlRootAttributetrên XmlSerializerctor có thể cung cấp gốc mong muốn và cho phép đọc trực tiếp vào Danh sách <>:

    // 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);
    }

Tín dụng: dựa trên câu trả lời từ YK1 .


11
Theo quan điểm của tôi, đây rõ ràng là câu trả lời cho câu hỏi. Câu hỏi là về việc khử lưu huỳnh vào Danh sách <T>. Tất cả các giải pháp khác, ngoại trừ có thể là một, bao gồm một lớp gói để chứa danh sách, đây chắc chắn không phải là câu hỏi được đăng, và những gì tác giả của câu hỏi dường như đang cố gắng tránh.
DDRider62

1
Với phương pháp này, XmlSerializerbộ nhớ cache phải được lưu trữ và sử dụng lại tĩnh để tránh rò rỉ bộ nhớ nghiêm trọng, hãy xem Rò rỉ bộ nhớ bằng cách sử dụng StreamReader và XmlSerializer để biết chi tiết.
dbc

16

Có, nó sẽ tuần tự hóa và giải tuần tự hóa Danh sách <>. Chỉ cần đảm bảo bạn sử dụng thuộc tính [XmlArray] nếu nghi ngờ.

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

Điều này hoạt động với cả serialize () và Deserialize ().


16

Tôi nghĩ rằng tôi đã tìm thấy một cách tốt hơn. Bạn không cần phải đặt thuộc tính vào các lớp học của bạn. Tôi đã thực hiện hai phương thức để tuần tự hóa và giải tuần tự hóa, lấy danh sách chung làm tham số.

Hãy xem (nó hoạt động với tôi):

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;
    }

Vì vậy, bạn có thể tuần tự hóa bất cứ danh sách nào bạn muốn! Bạn không cần chỉ định loại danh sách mỗi lần.

        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
Cảm ơn vì đã thực sự trả lời câu hỏi. Tôi sẽ thêm rằng cho List<MyClass>các yếu tố tài liệu nên được đặt tên ArrayOfMyClass.
Max Toro

8

Có, nó không được khử trong Danh sách <>. Không cần phải giữ nó trong một mảng và bọc / gói nó trong một danh sách.

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

    public UserHolder()
    {
    }

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

Mã khử

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

5

Không chắc chắn về Danh sách <T> nhưng Mảng chắc chắn là có thể. Và một chút phép thuật giúp bạn dễ dàng trở lại Danh sách.

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

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

2
Có thể làm mà không có lớp "người giữ"?
Daniel Schaffer

@Daniel, AFAIK, không. Bạn cần tuần tự hóa và giải tuần tự hóa thành một số loại đối tượng cụ thể. Tôi không tin rằng tuần tự hóa XML hỗ trợ các lớp tập hợp như là sự khởi đầu của tuần tự hóa. Tôi không biết 100% rằng mặc dù.
JaredPar

[XmlE bổ sung ("danh sách")] nên là [XmlArray ("danh sách")]. Đó là cách duy nhất Deserialization hoạt động với tôi trong .NET 4.5
eduardobr

2

Làm thế nào về

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);    

Không đặc biệt lạ mắt nhưng nó nên hoạt động.


2
Chào mừng bạn đến với stackoverflow! Luôn luôn tốt hơn để cung cấp một mô tả ngắn cho mã mẫu để cải thiện độ chính xác của bài đăng :)
Phần mềm Picrofo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.