XML生成の練習

やること

以下のXML(HTML)を、PowerShellを使って生成してみる。

<html lang="ja">
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <p>Hello <strong>W</strong>orld !!<br />made by PowerShell</p>
  </body>
</html>

f:id:imihito:20180120224636p:plain
生成htmlイメージ

(HTMLとして考えると<!DOCTYPE html>も必要だが後で何とかなるので置いておく)

操作方法の確認も兼ねて以下の二つの方法で作成してみる。

  • System.Xml.XmlWriterを使用する
  • System.Xml.XmlDocumentを使用する

System.Xml.XmlWriter

基本的にWriteStart○○メソッドで要素を書き出して、WriteEnd○○で閉じる。 閉じてない要素は最後で自動で閉じてくれるので省略可。

要素と中身が固定(追記が必要ない)場合はWrite○○Stringを使うと一行で済んで楽になる。

# XML書き出し先
[Text.StringBuilder]$sb = New-Object -TypeName Text.StringBuilder

# XmlWriter設定項目 インスタンス&プロパティ設定
[Xml.XmlWriterSettings]$setXw = 
    New-Object -TypeName Xml.XmlWriterSettings -Property @{
            OmitXmlDeclaration = $true #<?xml version="1.0"~を作成しない
            Indent             = $true #自動整形する
        }

[Xml.XmlWriter]$xw = [Xml.XmlWriter]::Create($sb, $setXw)

# htmlタグ
$xw.WriteStartElement("html")
$xw.WriteAttributeString("lang", "ja")
    
    # headタグ
    $xw.WriteStartElement("head")
        
        $xw.WriteStartElement("meta")
            $xw.WriteAttributeString("charset", "utf-8")
        $xw.WriteEndElement()

    $xw.WriteEndElement()

    # bodyタグ
    $xw.WriteStartElement("body")
        
        $xw.WriteStartElement("p")
            $xw.WriteString("Hello ")
            $xw.WriteElementString("strong", "W")
            $xw.WriteString("orld !!")

            $xw.WriteElementString("br", $null)
            $xw.WriteString("made by PowerShell")


$xw.Close() # 自動タグ閉じ

# 結果確認
$sb.ToString()

# ファイル出力
<#
[string]$savePath = [IO.Path]::Combine(
        [Environment]::GetFolderPath("MyDocuments"),
        "XmlWriter.html"
    )

[IO.File]::WriteAllText(
    $savePath,
    $sb.ToString(),
    [Text.UTF8Encoding]$false
)

explorer.exe $savePath
#>

System.Xml.XmlDocument

Xml.XmlDocumentCreateElementメソッドで要素を作成し、それを各要素にAppendChildで追加していく。

各要素がオブジェクトとして返るので、その分変数が必要。

# 文字列をXml.XmlDocument型にキャスト(型エイリアス[xml])
[xml]$doc = [xml]"<html />"

# htmlタグ
[Xml.XmlNode]$ndHtml = $doc.FirstChild
$ndHtml.SetAttribute("lang", "ja")

# headタグ
[Xml.XmlNode]$ndHead = $ndHtml.AppendChild($doc.CreateElement("head"))
    
    $ndHead.AppendChild(
        $doc.CreateElement("meta")
    ).SetAttribute("charset", "utf-8")

# bodyタグ
[Xml.XmlNode]$ndBody = $ndHtml.AppendChild($doc.CreateElement("body"))

    [Xml.XmlNode]$ndP = $ndBody.AppendChild($doc.CreateElement("p"))
        $ndP.AppendChild($doc.CreateTextNode("Hello ")) > $null
        $ndP.AppendChild($doc.CreateElement("strong")).InnerText = "W"
        $ndP.AppendChild($doc.CreateTextNode("orld !!")) > $null
        
        $ndP.AppendChild($doc.CreateElement("br")) > $null
        $ndP.AppendChild($doc.CreateTextNode("made by PowerShell")) > $null


# 出力準備
[Text.StringBuilder]$sb = New-Object -TypeName Text.StringBuilder
[Xml.XmlWriterSettings]$setXw = 
    New-Object -TypeName Xml.XmlWriterSettings -Property @{
            OmitXmlDeclaration = $true #<?xml version="1.0"~を作成しない
            Indent             = $true #自動整形する
        }

$doc.Save([Xml.XmlWriter]::Create($sb, $setXw))

# 結果確認
$sb.ToString()

# ファイル出力
<#
[string]$savePath = [IO.Path]::Combine(
        [Environment]::GetFolderPath("MyDocuments"),
        "XmlDocument.html"
    )

[IO.File]::WriteAllText(
    $savePath,
    $sb.ToString(),
    [Text.UTF8Encoding]$false
)

explorer.exe $savePath
#>

感想

今回の例のように純粋に生成するだけならXml.XmlWriterを使った方が楽そう。

.NET系だと LINQ to XML を使う方法があるらしいけど、なかなか使う機会が無いので一旦スルー。