XML을 List <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; }
}

XmlSerializerXML을 deserialize하는 데 사용할 수 있습니까?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")]를 사용하는 것이 좋습니다. 이것을보고, 나는 그것이 <user> 또는 <Items> 노드 (XmlElement 속성이없는 경우)를 방출 한 다음 그 아래에 <user> 노드를 추가 할 것이라고 생각했습니다. 그러나 나는 그것을 시도했지만 그것을하지 않았으므로 질문이 원하는 것을 정확하게 방출했습니다.
Jon Kragh

위의 UserList 아래에 두 개의 목록이 있으면 어떻게합니까? 나는 당신의 방법을 시도하고 이미 같은 매개 변수 유형과 XYZ라는 멤버 정의 말한다
칼라 J

왜 이것이 정답으로 표시되는지 모르겠습니다. 목록을 래핑 할 클래스를 추가하는 것도 포함됩니다. 그것은 확실히 질문이 피하려고하는 것입니다.
DDRider62

1
@ DDRider62 질문은 "포장없이"라고 말하지 않습니다. 대부분의 사람들은 매우 실용적이며 데이터를 꺼내고 싶어합니다. 이 답변을 통해 .Items회원을 통해 그렇게 할 수 있습니다 .
Marc Gravell

50

필요한 대문자와 일치하도록 User클래스 를 장식하면 XmlType:

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

그런 다음 XmlRootAttributeXmlSerializerctor에 원하는 루트를 제공하고 목록 <>에 직접 읽기를 허용 할 수 있습니다 :

    // 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
제 관점에서 이것은 분명히 질문에 대한 답입니다. 문제는 List <T>로 직렬화 해제하는 것에 관한 것입니다. 어쩌면 하나를 제외한 다른 모든 솔루션에는 목록을 포함하는 래핑 클래스가 포함되어 있습니다.
DDRider62

1
이 접근 방식을 사용하면 XmlSerializer심각한 메모리 누수를 피하기 위해 정적으로 캐시하고 재사용해야합니다 . 자세한 내용은 StreamReader 및 XmlSerializer사용하여 메모리 누수를 참조 하십시오.
dbc

16

예, List <>를 직렬화하고 역 직렬화합니다. 확실하지 않은 경우 [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.
Max Toro

8

예, List <>로 직렬화 해제합니다. 배열에 보관할 필요가없고 목록으로 감싸거나 캡슐화 할 필요가 없습니다.

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

List <T>에 대해서는 확실하지 않지만 배열은 확실히 가능합니다. 그리고 약간의 마술로 인해 목록에 다시 쉽게 도달 할 수 있습니다.

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

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

2
"홀더"클래스 없이도 가능합니까?
Daniel Schaffer

@ 다니엘, AFAIK, 아니오. 구체적인 객체 유형으로 직렬화하고 역 직렬화해야합니다. XML 직렬화가 기본적으로 직렬화의 시작으로 컬렉션 클래스를 지원한다고 생각하지 않습니다. 나는 100 % 알지 못합니다.
JaredPar

대신 [XmlElement ( "list")]는 [XmlArray ( "list")] 여야합니다. 이것이 .NET 4.5에서 deserialization이 작동 한 유일한 방법입니다.
eduardobr 03.11. 16.

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 소프트웨어
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.