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

を使用XmlSerializerしてxmlを非シリアル化してにでき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>ノードを追加すると思いました。しかし、私はそれを試してみましたが、しなかったので、質問が望んでいたものを正確に発しました。
ジョンクラー

上記のUserListに2つのリストがある場合はどうなりますか?私はあなたの方法を試しました、そしてそれは同じパラメータータイプでXYZと呼ばれるメンバーをすでに定義していると言います
Kala J

なぜこれが正解としてマークされているのかわかりません。リストをラップするクラスを追加します。それは確かに質問が避けようとしていることでした。
DDRider62 2016

1
@ DDRider62の質問は「折り返しなし」とは言いません。ほとんどの人はかなり実用的で、データを取り出したいだけです。この回答により、.Itemsメンバーを介してそれを行うことができます。
マークグラベル

50

必要な大文字と一致するようにUserクラスをで装飾する場合XmlType

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

次に、Actor のXmlRootAttributeオンでXmlSerializer目的のルートを提供し、List <>に直接読み取ることができます。

    // 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>への逆シリアル化についてでした。他のすべてのソリューションは、おそらく1つを除いて、リストを含むためのラッピングクラスを含みます。これは確かに投稿された質問ではなく、質問の作成者が回避しようとしているようです。
DDRider62 2016

1
このアプローチでは、 XmlSerializer、重大なメモリリークを回避するためにを静的にキャッシュして再利用する必要があります。詳細については、StreamReaderとXmlSerializer使用したメモリリークを参照してください。
DBC

16

はい、List <>をシリアル化および逆シリアル化します。疑わしい場合は、必ず[XmlArray]属性を使用してください。

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

これはSerialize()とDeserialize()の両方で機能します。


16

私はもっ​​と良い方法を見つけたと思います。クラスに属性を配置する必要はありません。ジェネリックリストをパラメーターとして取る、シリアル化と逆シリアル化の2つのメソッドを作成しました。

見てみましょう(私にとってはうまくいきます):

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、AFAIK、いいえ。いくつかの具象オブジェクトタイプにシリアライズおよびデシリアライズする必要があります。XMLシリアル化が、シリアル化の開始時にコレクションクラスをネイティブでサポートしているとは思いません。私はそれを100%知りません。
JaredPar

[XmlElement( "list")]は、代わりに[XmlArray( "list")]にする必要があります。これが、.NET 4.5での
逆シリアル化

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へようこそ!投稿の精度を向上させるために、サンプルコードの短い説明を常に提供することをお
勧めし
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.