C# で XML を読み込んで値を取得する

Azure の Public の Blob のリストを取得したいんだけど、Azure CLI や、Azure SDKなし、つまり認証なしでやりたいと思っていました。実際に調べてみると、出来るようです。

* [List Blob](https://docs.microsoft.com/en-us/rest/api/storageservices/list-blobs)

ところが、人生は甘くないようで、戻り値が、`XML`のようです。おお、、、めんどくさい香りしかしない、、、ところが、C#は意外にXMLに強いようで意外と簡単に行けました。その説明をしてみたいと思います。

# 解析したい XML ファイル

上記のAPIの戻り値は次のようなXMLです。やりたいことは、ここのUrlに記述されている値を取得したいなと思います。

```xml
<EnumerationResults ContainerName="https://some.blob.core.windows.net/healthcheck">
<Blobs>
    <Blob>
        <Name>azure-functions-core-tools.json</Name>
        <Url>https://some.blob.core.windows.net/healthcheck/azure-functions-core-tools.json</Url>
        <Properties>
            <Last-Modified>Sun, 07 Jun 2020 01:53:41 GMT</Last-Modified>
            <Etag>0x8D80A859AB1ED42</Etag>
            <Content-Length>88</Content-Length>
            <Content-Type>application/json</Content-Type>
            <Content-Encoding/>
            <Content-Language/>
            <Content-MD5>a+BCgjwVn3JqvjG3MezREQ==</Content-MD5>
            <Cache-Control/>
            <BlobType>BlockBlob</BlobType>
            <LeaseStatus>unlocked</LeaseStatus>
        </Properties>
    </Blob>
    <Blob>
        <Name>azure-functions-host.json</Name>
        <Url>https://some.blob.core.windows.net/healthcheck/azure-functions-host.json</Url>
        <Properties>
            <Last-Modified>Sun, 07 Jun 2020 01:53:41 GMT</Last-Modified>
            <Etag>0x8D80A859AB3E96C</Etag>
            <Content-Length>88</Content-Length>
            <Content-Type>application/json</Content-Type>
            <Content-Encoding/>
            <Content-Language/>
            <Content-MD5>EktDgZOalVaExkgWs8JqgQ==</Content-MD5>
            <Cache-Control/>
            <BlobType>BlockBlob</BlobType>
            <LeaseStatus>unlocked</LeaseStatus>
        </Properties>
    </Blob>
</Blobs>
<NextMarker/>
</EnumerationResults>
```

# 実装

## XMLの読み込み
最初のAPIの説明の通りにリクエストを実施します。HttpClient すらも不要です。これは簡単ですね。

```csharp
var list = XDocument.Load("https://some.blob.core.windows.net/healthcheck?restype=container&comp=list")
```

## 要素の取得
今回は、`Blob` の下にある `Url` を取得したいです。階層会ってめんどくさそうやなと思いきや、Linqが使えるようです。

```csharp
var names = list.Descendants("Blob").Select(p => p.Element("Url")?.Value);
``` 

[LINQ to XML ](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml-overview)に記述があるのですが、`list`は[XmlDocument](https://docs.microsoft.com/en-us/dotnet/api/system.xml.xmldocument?view=netcore-3.1)のオブジェクトなのですが、このクラスには`decendents`メソッドはありません。これは、`System.Xml.Linq.XName`パッケージの拡張メソッドです。このXMLドキュメントの`decendents`つまりルートから考えた時の子孫を検索して、`IEnumerable<XElement>`として返却してくれます。IEnumerableが帰るので、Linqし放題です。更新系のメソッドなども存在するので、ご興味がある人は[XContainer Class](https://docs.microsoft.com/en-us/dotnet/api/system.xml.linq.xcontainer?view=netcore-3.1)をご覧ください。

### 子要素の取得

私は `Element("Url")?.Value`で、`Blob`の子Elementである`Url`を参照しています。

### アトリビュートの取得

アトリビュート、つまり、XMLのアトリビュート、下記の例では `Address`の`Type=Shipping`を取得したい場合は、`p.Attribute("Type")` で取得することができます。

```xml
  <Address Type="Shipping">  
    <Name>Ellen Adams</Name>  
    <Street>123 Maple Street</Street>  
    <City>Mill Valley</City>  
    <State>CA</State>  
    <Zip>10999</Zip>  
    <Country>USA</Country>  
  </Address>  
```
# おわりに

今回は読込だけを知りたかったのでこの程度の調査で終了しますが、忘れないように記述しておきました。思ったよりずっと簡単でよかったです。