35007


System.Xml.Serialization.XmlSerializer.Deserialize(StreamReader)を使って、  
任意のxmlファイルのrootタグにあるアトリビュート値を取り出したいです。

以下のようにしてデシリアライズしないで取得できることは分かっていますが、  
デシリアライズ用のクラスA（アトリビュート名のプロパティを持つだけのクラス）  
を作成して、デシリアライズして、A.アトリビュート名というような  
感じで取得できないでしょうか？

うまくいかなくて悩んでいます。

さまざまな形式のXMLがありますが、ルートアトリビュートだけは同じXMLを読み込もうとしています。

[code]

    // XMLドキュメントを開く
    XmlDocument doc = new XmlDocument();
    doc.Load(path);
    
    // ルートの要素を取得
    XmlElement xmlRoot = doc.DocumentElement;
    stirng attribute = xmlRoot.GetAttribute("アトリビュート名");
    
[/code]

Aクラスは以下のような感じを考えています。

[code]

    public class A
    {
        [System.Xml.Serialization.XmlAttribute("アトリビュート名")]
        public string アトリビュート名{ get; set; }
    }
    
[/code]

説明を追加させていただきます。  
XMLの例としては以下のような感じです。二つのXMLに対して、同じAクラスを用いて  
デシリアライズしたいのですが、  
System.Xml.Serialization.XmlSerializer.Deserialize(StreamReader)  
をコールすると一つ目のXMLをデシリアライズする際に以下のExceptionとなってしまいます。

[code]

    [System.InvalidOperationException] = {"<RootTag xmlns=''> は指定できません。"}
    
[/code]

1つ目のXML

[code]

    <RootTag アトリビュート名="">
       <AAA/>
       <BBB/>
    </RootTag>
    
[/code]

2つ目のXML

[code]

    <RootTag2 アトリビュート名="">
       <CCC/>
       <DDD/>
    </RootTag2>
    
[/code]


CC BY-SA 3.0
2017-05-25T05:10:41.287
35001
2017-05-25T09:26:54.823
2017-05-25T07:24:31.893
9228
9228
question
c#
C# XmlSerializerを使って、任意のxmlファイルのattribute値を取得したい


事前にルート要素のノード名が分かっていれば、[XmlRootAttributeを指定](https://stackoverflow.com/a/17702303)することで対応できます。  
下記の例ではルート要素以外は適当に定義していますが、正常に動作して属性をプロパティに読み込みます。  
サンプルコードは[デシリアライズのサンプル](http://d.hatena.ne.jp/tekk/20090823/1251030040)を流用して作成しました。

[code]

    using System;
    using System.IO;
    using System.Text;
    using System.Xml.Serialization;
    
    namespace ConsoleApplication3
    {
        class Program
        {
            static void Main(string[] args)
            {
                var xml1 = @"<RootTag アトリビュート名=""Hoge""><AAA/><BBB/></RootTag>";
                DoSample("RootTag", xml1);
                var xml2 = @"<RootTag2 アトリビュート名=""Fuga""><CCC/><DDD/></RootTag2>";
                DoSample("RootTag2", xml2);
            }
    
            private static void DoSample(string rootName, string xml)
            {
                var fileName = Guid.NewGuid().ToString() + ".xml";
                //XML書き出すだけ
                WriteXml(fileName, xml);
                try
                {
                    //XML読み込み
                    ReadXml(fileName, rootName);
                }
                finally
                {
                    //後処理
                    File.Delete(fileName);
                }
            }
    
            private static void ReadXml(string fileName, string rootName)
            {
                var root = new XmlRootAttribute { ElementName = rootName };
                var serializer = new XmlSerializer(typeof(A), root);
                using (var reader = new StreamReader(fileName, Encoding.UTF8))
                {
                    //キャスト
                    var item = (A)serializer.Deserialize(reader);
                    Console.WriteLine(item.アトリビュート名);
                }
            }
    
            private static void WriteXml(string fileName, string xml)
            {
                using (var writer = new StreamWriter(fileName))
                {
                    writer.Write(xml);
                }
            }
        }
        [XmlRoot]
        public class A
        {
            [XmlAttribute("アトリビュート名")]
            public string アトリビュート名 { get; set; }
        }
    }
    
[/code]


CC BY-SA 3.0
2017-05-25T06:06:18.827
35003
2017-05-25T08:12:35.100
2017-05-25T08:12:35.100
9820
9820
35001
answer


一般論として、シリアライザーはクラスとシリアライズ結果とが１対１で対応します。そのため、`<RootTag>`に対応する`RootTag`クラスと`<RootTag2>`に対応する`RootTag2`クラスを用意する必要があります。  
内容が同じということであれば、共通の親クラスを用意しても構いません。

[`XmlSerializer`](https://msdn.microsoft.com/ja-
jp/library/system.xml.serialization.xmlserializer\(v=vs.110\).aspx)で説明すると各クラスを用意します。

[code]

    public class Root { public string アトリビュート名 { get; set; } }
    public class RootTag : Root {}
    public class RootTag2 : Root {}
    
[/code]

その上でそれぞれ適切なシリアライザーのインスタンスを生成します。

[code]

    var xs1 = new XmlSerializer(typeof(RootTag));
    var xs2 = new XmlSerializer(typeof(RootTag2));
    
[/code]

これはXMLファイルの種類と同じだけのクラス・シリアライザーを用意する必要があり、不本意かと思います。しかし、そもそもシリアライザーの適さない用途に無理やり使用していることが問題ですので、個人的には
**シリアライザーを使用しない** ことをお勧めします。

* * *

どうしてもシリアライザーを使わざるを得ない場合の別解として

`XmlSeriailzer`ではルート要素については[`[XmlRoot]`](https://msdn.microsoft.com/ja-
jp/library/system.xml.serialization.xmlrootattribute\(v=vs.110\).aspx)で指定します。

[code]

    [XmlRoot("RootTag")]
    public class Root { public string アトリビュート名 { get; set; } }
    
[/code]

ところが`[XmlRoot]`を複数指定することができないため、このままではうまくいきません。  
幸い、[`XmlSeriailzer`コンストラクター](https://msdn.microsoft.com/ja-
jp/library/f1wczcys\(v=vs.110\).aspx)が`[XmlRoot]`を引数に受け付けています。

[code]

    public class Root { public string アトリビュート名 { get; set; } }
    
[/code]

[code]

    var xs1 = new XmlSerializer(typeof(Root), new XmlRootAttribute("RootTag"));
    var xs2 = new XmlSerializer(typeof(Root), new XmlRootAttribute("RootTag2"));
    
[/code]

としてやれば、クラス定義だけは１つにまとめることができます。

* * *

もし.NET Framework 1.0から存在する`System.Xml`名前空間の`XmlDocument`等のクラス群によるXML
DOM操作が煩雑というのがシリアライザーを使用する要因でしたら、.NET Framework 3.5で導入された[Linq to
XML](https://msdn.microsoft.com/ja-
jp/library/bb387098\(v=vs.110\).aspx)の使用をお勧めします。  
質問冒頭の処理は

[code]

    var attribute = (string)XDocument.Load(path).Root.Attribute("アトリビュート名");
    
[/code]

と記述できます。


CC BY-SA 3.0
2017-05-25T08:20:24.620
35007
2017-05-25T09:26:54.823
2017-05-25T09:26:54.823
4236
4236
35001
answer