XML書き込み

前回は何故かiniファイルに飛んでしまいましたが、今度こそXML書き込みです。

今回は、第5章で使用したXMLデータを1から作成して保存する、 みたいな事をやってみます。 まあ、実際、XMLファイルを1から作る機会なんて余り無いかもしれませんが、 前回のXML読み込みと組み合わせれば、読み込んだXMLを編集して保存する事も 可能になりますし、 いつか、銃を突きつけられて 「もし、1からXMLを作成する方法を知っていたら見逃してやる」 みたいな状況になった時にありがたみを感じるかもしれませんので。(異次元)

ちなみに、作成しようとするデータはこんな感じ。

<?xml version="1.0" encoding="Shift_JIS" ?>
<school name="わかめ">
  <grade>高校</grade>
  <club name="セクシーコマンドー部">
    <members>
      <member>マサルさん</member>
      <member>フーミン</member>
      <member>マチャ彦</member>
      <member>校長</member>
      <member>キャシャリン</member>
      <member>モエモエ</member>
      <member>アフロくん</member>
    </members>
  </club>
  <club name="野球部">
    <members>
      <member>川島</member>
      <member>目立たない人々</member>
    </members>
  </club>
</school>

で、これを作成しようとするとこんな感じになったりします。

Option Explicit
 
Dim xmlDoc  'XMLのDomDocumentオブジェクト
Dim xmlNode  'XMLのNodeオブジェクト
Dim objRoot  'Rootノード保持用
 
'XMLDOMDocument作成
Set xmlDoc = WScript.CreateObject("MSXML2.DOMDocument")
 
'XML宣言
Set xmlNode = xmlDoc.createProcessingInstruction( _
    "xml", "version=""1.0"" encoding=""Shift_JIS""")
xmlDoc.appendChild xmlNode
 
Set xmlNode = xmlDoc
 
'Rootノード(school)
Set objRoot = CreateElementNode(xmlNode,"school","")
CreateElementNode objRoot,"grade","高校"
 
'セクシーコマンドー部
Set xmlNode = CreateElementNode(objRoot,"club","")
CreateAttribute xmlNode,"name","セクシーコマンドー部"
 
'セクシーコマンドー部メンバー
Set xmlNode = CreateElementNode(xmlNode,"members","")
CreateElementNode xmlNode,"member","マサルさん"
CreateElementNode xmlNode,"member","フーミン"
CreateElementNode xmlNode,"member","マチャ彦"
CreateElementNode xmlNode,"member","校長"
CreateElementNode xmlNode,"member","キャシャリン"
CreateElementNode xmlNode,"member","モエモエ"
CreateElementNode xmlNode,"member","アフロくん"
 
'野球部
Set xmlNode = CreateElementNode(objRoot,"club","")
CreateAttribute xmlNode,"name","野球部"
 
'野球部メンバー
Set xmlNode = CreateElementNode(xmlNode,"members","")
CreateElementNode xmlNode,"member","川島"
CreateElementNode xmlNode,"member","目立たない人々"
 
xmlDoc.Save Left(WScript.ScriptName, _
  InstrRev(WScript.ScriptName,".") - 1) & ".xml"
 
Set xmlDoc = Nothing
 
'XMLDOMNodeを作成
Function CreateElementNode(parentNode, tagName, Text)
  Dim objNode 'XMLのNodeオブジェクト
  
  Set objNode = _
      parentNode.selectSingleNode("/").createElement(tagName)
  If Text <> "" Then objNode.Text = Text
  parentNode.appendChild objNode
  
  Set CreateElementNode = objNode
End Function
 
'XMLDOMAttributeを作成
Function CreateAttribute(targetNode, attributeName, Text)
  Dim objAttr 'XMLのNodeオブジェクト
  
  Set objAttr = _
    targetNode.selectSingleNode("/").CreateAttribute(attributeName)
  objAttr.Text = Text
  targetNode.Attributes.setNamedItem objAttr
  
  Set CreateAttribute = objAttr
End Function

では、少しずつ説明していきます。 が、まずは下の方にある関数を無視して概要を説明します。 その後、関数についても説明します。

'XMLDOMDocument作成
Set xmlDoc = WScript.CreateObject("MSXML2.DOMDocument")

XML読み込みの時と同様にまずは、XMLドキュメント自体を作成します。 読み込みの場合は、この後XMLドキュメントオブジェクトに対して ファイルを指定してファイルの内容をロードしましたが、 書込みでは、この後、実際にXMLを書き込んでいきます。

'XML宣言
Set xmlNode = xmlDoc.createProcessingInstruction( _
    "xml", "version=""1.0"" encoding=""Shift_JIS""")
xmlDoc.appendChild xmlNode

ここで、XMLの

<?xml version="1.0" encoding="Shift_JIS" ?>

の、部分を作成しています。 XMLドキュメントオブジェクトのcreateProcessingInstructionと言う メソッドで<? ?>と言うタグが作成可能です。 引数の2つ目に属性を指定する事ができますが、属性が1つの場合も、 2つ以上の場合も単純に属性自体も含めた文字列を指定します。

WSHで文字列は、"文字列"として表されますが、 表したい文字列の中に"が入っていた場合、 その時点までが文字列と判断されてしまい、エラーとなってしまいます。 上記の2つ目の引数のような状況がこれにあたります。 回避するには、文字列中の"は、""とします。 2つ連続して""と書くと、WSHは"だと解釈します。 「じゃあ、本当に2つ""と表したい場合は?」と言うと、 """"と、合計4つ連続で書く事になります。

以下、XMLノードに対して子ノードを追加していく処理となります。

Set xmlNode = xmlDoc
 
'Rootノード(school)
Set objRoot = CreateElementNode(xmlNode,"school","")
CreateElementNode objRoot,"grade","高校"

最初に一番親のノードとしてXMLドキュメント自体を設定します。 で、CreateElementNode関数でノードを追加します。

この関数は、

CreateElementNode(親ノード,ノードの名前,ノードの値)

と言う使い方です。 見れば大体分かると思いますが、要するに 親ノードノードの値を設定したノードの名前のノードを追加しろ、っつーことです。戻り値には作られたノードが返ってきます。 具体的には、

<親ノード>
  <ノードの名前>ノードの値</ノードの名前>
</親ノード>

みたいな感じです。ノードの値に文字列を設定しない(空文字を設定)すると、ノードだけが追加されます。↓こんな感じ。

<親ノード>
  <ノードの名前/>
</親ノード>

WSHでは関数の引数を省略できない為、こんな感じの仕様にしてます。

と言う訳で、この処理で最初の<school>ノードを作っているわけです。 作ったノードはobjRootにて保持されます。

'セクシーコマンドー部
Set xmlNode = CreateElementNode(objRoot,"club","")
CreateAttribute xmlNode,"name","セクシーコマンドー部"

次、<club>です。<club>の親ノードは<school>、 つまり、先ほど保持しておいたobjRootですので、親ノードの部分には objRootを指定します。

もう1つ関数が出てきました。CreateAttributeです。 これもさっきのと似たようなもんですが、違うのはノードではな属性(くアトリビュート)を追加する、と言うものです。

CreateAttribute(対象ノード,属性の名前,属性の値)

引数もCreateElementNodeとほとんど同じです。 で、これで"セクシーコマンドー部"を追加してます。

'セクシーコマンドー部メンバー
Set xmlNode = CreateElementNode(xmlNode,"members","")
CreateElementNode xmlNode,"member","マサルさん"
CreateElementNode xmlNode,"member","フーミン"
CreateElementNode xmlNode,"member","マチャ彦"
CreateElementNode xmlNode,"member","校長"
CreateElementNode xmlNode,"member","キャシャリン"
CreateElementNode xmlNode,"member","モエモエ"
CreateElementNode xmlNode,"member","アフロくん"

同様に、メンバーを追加していきます。 各関数には戻り値がありますが、特に戻り値を必要としない場合は、 変数に代入する必要はありません。 が、その代わり、関数の後の()も省略します。 関数名(引数)とやってしまうと、WSHでは戻り値を代入する変数が設定されている事を期待するようになっている為エラーとなります。 戻り値を捨てる場合は、関数名 引数として、()を取り除きます。

つづく野球部の部分は全く同じなので説明省略。

xmlDoc.Save Left(WScript.ScriptName, _
  InstrRev(WScript.ScriptName,".") - 1) & ".xml"
 
Set xmlDoc = Nothing

最後に、Saveメソッドを使用してファイルに保存します。 既にある場合は上書きされるので注意。 ここでは、現在実行している .vbs ファイルの拡張子を .xml に変更したファイルに保存しています。

以前の章ではこーゆー場合、FileSystemObjectを利用してちゃんと(?)ファイルパスを作成していましたが、今回のスクリプトでは他の部分で一切FileSystemObjectを使用していないため、ここだけの為に生成するのもなんなので、FileSystemObjectを利用しない方法でやってます。

まず、WScript.ScriptNameは実行しているスクリプトのファイル名です。 例えば、lesson7.vbs とか wakame.vbs とか。

続いてLeft関数は、文字列を左(Left)から指定された文字数だけ切り取る関数です。

Left(対象文字列,欲しい文字数)

と言った感じ。*1 なので、例えばスクリプト名がcurry.vbsだった場合、curryは5文字なので、

xmlDoc.Save Left(WScript.ScriptName,5) & ".xml"

と、すればcurry.xmlとなります。(印度) が、もし、スクリプト名がlesson7.vbsだった場合は、lesso.xmlと言った感じで固い感じになってしまいます。messon7.vbsだったらmesso.xmlと言った感じでメソが何だかイタリアンな感じさえなってしまいます。

つまり、WScript.ScriptNameはやってみないと何文字になるかが分からないので、そのときに応じて動的に判断しなくてはならない、ということです。動的です。ダイナミックです。ダイクマです。

で、どうすりゃいいかっつーと、要するに「"."のあるところまで切り出せばよい」と、いう事になります。この、特定の文字列を見つけ出す関数が、 InStr関数です。

InStr(対象文字列,見つけたい文字列)

で、何文字目にあるかを戻してきます。 が、実はこれではちょっとまずいです。例えば、D.V.D!D.V.D!.vbsみたいなファイル名だった場合、2が返ってきてしまいます。

しかし、Left関数に対してRight関数があるように、InStr関数に対しても InStrRev関数があります。RevはRevolutionの略です。(革 命) ウソです。Reverse(逆)の略です。

つまり、前からではなく後ろから順番に検索してくれます。 なので、最後の"."にヒットしてくれる、と言うわけです。 wakame.vbsだった場合、7(文字目)と返してくるので、 そこから1文字引くと、6文字(=wakame)となり、丁度よい感じになります。

と、いう事で、

Left(WScript.ScriptName,InstrRev(WScript.ScriptName,".") - 1)

と、しているわけです。

以上が基本的なXML作成です。 続きまして、下の方にある2つの関数の説明を、、、、と思いましたが、 やっぱやめます。(えーー!!)

この講座全体に言える事ですが、「できりゃいい」んです。(適 当) もし、関数の中身まで興味があったら調べてみて下さい。 それでもし分からなかったら、コメント欄にでも書き込んでみてください。

と言う訳で、XML書き込み終了です。

次回も「要望があれば」と言うことにします。
考えてるのはXML,ADO,UDLによる各種データ操作、ADSI、WMI、IEの自動化等を考えてます。

この記事は訳に立ちましたか?

選択肢 投票
はい 71  
いいえ 16  

需要があれば押してください

選択肢 投票
続きが読みたい 94  
あれば読むかも 3  
別にいい 0  


WSHは別途 "10行くらい" と言うコーナーもあります。よろしければ。


  • 関数内で、DOMDocumentが取りたくてparentNode.selectSingleNode("/") 見たいな事やってるんですが、何か強引な気がしてます。もっといい方法がありそうです。 -- メソ 2006-04-06 (木) 17:49:01
  • MSXML2.DOMDocumentの書き込みを解説しているサイトを探しておりましたが、このページのおかげで目からウロコです。 -- ガッツ? 2008-02-04 (月) 16:40:41
  • ASPでのXML操作について、情報を求めて彷徨い続け、ついにここに辿り着きました。シンプルで分かりやすい、まさにこんなコードが欲しかった!ありがとうございます。 -- ぱぱいや? 2009-12-02 (水) 16:56:07

URL B I U SIZE Black Maroon Green Olive Navy Purple Teal Gray Silver Red Lime Yellow Blue Fuchsia Aqua White

*1 同様にRight関数(右から)なんてのもあります。