XMLファイルを読み込み、各種変更して、XMLファイルとして保存
以下で作成したXMLファイルの追加解説です。
以下の変換前のXMLファイルで説明します。
<?xml version="1.0" encoding="UTF-8"?>
<all>
<row>
<A attrid="attrV">
<AA>データ1</AA>
<C id="text">データ2</C>
</A>
<D>
<E>データ3</E>
<F>データ4</F>
</D>
<G>
<H>
<I klm="post">
<J year="2024">データ5</J>
<K>データ6</K>
</I>
<L>データ7</L>
</H>
<M>データ8<N>データ9</N>
</M>
</G>
<O>データ10</O>
</row>
<row>
<A attrid="attrV">
<AA>データ11</AA>
<C id="text">データ12</C>
</A>
<D>
<E>データ13</E>
<F>データ14</F>
</D>
<G>
<H>
<I klm="post">
<J year="2024">データ15</J>
<K>データ16</K>
</I>
<L>データ17</L>
</H>
<M>データ18<N>データ19</N>
</M>
</G>
<O>データ20</O>
</row>
<row>
<A attrid="attrV">
<AA>21</AA>
<C id="text"/>
</A>
<D>
<E>23<br>B</E>
<F>24</F>
</D>
<G>
<H>
<I klm="post">
<J year="2024"/>
<K>26</K>
</I>
<L/>
</H>
<M>28<N/>
</M>
</G>
<O/>
</row>
</all>
このXMLファイルに、以下の変更します。
- コメントが消えてしまうので、ここで追加。<row>タグの前に、改行+<!– row: 「行数」 –>を追加
- タグの中の「<」「>」がエスケープ文字「&+lt;」「&+gt;」(この文字は、ここから「+」を削除して読んでください)に変換されてしまうので、ここで元に戻す
- 空要素があれば、省略した書式ではなく、タグで囲むように変更
- 改行コードを、「vbLf」に置換します
そのサンプルマクロは、以下です。
' https://www.depthbomb.net/?p=6917
' 引数で渡されたXMLファイルを読み込み、各種変更して、XMLファイルとして保存します。
' <変更点>
' コメントの追加、エスケープ文字を戻す、タグで囲むように変更、改行コードを「vbLf」に置換
Private Sub ReCreateXMLFile(TargetFilePath As String, OutputFilePath As String)
Dim LineText As String
Dim AllText As String
Dim num As Long
Dim rowNum As Long: rowNum = 1
Dim myComment As String
'Stramオブジェクトを生成して変換対象のファイルを読み込みます。
With CreateObject("ADODB.Stream")
.Charset = "UTF-8"
.Open
.LoadFromFile TargetFilePath
Do Until .EOS
LineText = .ReadText(-2)
' コメントが消えてしまうので、ここで追加
If InStr(LineText, "<row") > 0 Then
myComment = "<!-- row: " & rowNum & " -->"
LineText = vbCrLf & vbTab & myComment & vbCrLf & LineText
rowNum = rowNum + 1
End If
' タグの中の「<」「>」がエスケープ文字「<」「>」に変換されてしまうので、ここで元に戻す
If InStr(LineText, "<") > 0 Then
LineText = Replace(LineText, "<", "<")
End If
If InStr(LineText, ">") > 0 Then
LineText = Replace(LineText, ">", ">")
End If
' 空要素があれば、省略した書式ではなく、タグで囲むように変更。
If Right(LineText, 2) = "/>" Then
' 空要素を、タグを囲む文字列に変換
LineText = ConvertToEnclosedInTag(LineText) & vbCrLf
Else
LineText = LineText & vbCrLf
End If
' 改行コードを、「vbLf」に置換します。
' 以下、最終のXMLファイル
AllText = AllText & Replace(LineText, vbCrLf, vbLf)
Loop
.Close
End With
'再度Streamオブジェクトを生成して読み込んだテキストからBOMを排除して書き込みます。
With CreateObject("ADODB.Stream")
.Charset = "UTF-8"
.Open
.WriteText AllText, adWriteChar '文字列を書き込み
.Position = 0 'ストリームの位置を0にする
.Type = adTypeBinary 'データの種類をバイナリデータに変更
.Position = 3 'ストリームの位置を3にする
Dim byteData() As Byte '一時格納用
byteData = .Read 'ストリームの内容を一時格納用変数に保存
.Close '一旦ストリームを閉じる(リセット)
.Open 'ストリームを開く
.Write byteData 'ストリームに一時格納したデータを流し込む
.SaveToFile OutputFilePath, adSaveCreateOverWrite '上書き保存
.Close
End With
End Sub
' 空要素を、タグを囲む文字列に変換
' <M>28<N/> → <M>28<N></N>
' <C id="text"/> → <C id="text"></C>
Private Function ConvertToEnclosedInTag(ByVal str As String) As String
' 「/>」の前の「<」を探す。
Dim m As Long, n As Long
n = InStrRev(str, "/>")
m = InStrRev(str, "<", n)
' "<" と "/>" の中の文字列
Dim txt As String
txt = Mid(str, m + 1, n - m - 1)
' 文字列に半角空白がある場合は、そこまでの文字列を取得
Dim txt2 As String
Dim txt2Num As Long
txt2Num = InStr(txt, " ")
If txt2Num > 0 Then
txt2 = Left(txt, txt2Num - 1)
Else
txt2 = txt
End If
ConvertToEnclosedInTag = Replace(str, "<" & txt & "/>", "<" & txt & "></" & txt2 & ">")
End Function
このマクロは、https://www.depthbomb.net/?p=6917 を参考にしています。
以下、使用例です。
Private Sub 使用例()
Call BackupReCreateXMLFile(ThisWorkbook.path & "\変換前.xml", ThisWorkbook.path & "\変換後.xml")
End Sub
このマクロを実行すると、「変換前.xml」ファイルが、変更され、以下の「変換後.xml」が作成されます。
<?xml version="1.0" encoding="UTF-8"?>
<all>
<!-- row: 1 -->
<row>
<A attrid="attrV">
<AA>データ1</AA>
<C id="text">データ2</C>
</A>
<D>
<E>データ3</E>
<F>データ4</F>
</D>
<G>
<H>
<I klm="post">
<J year="2024">データ5</J>
<K>データ6</K>
</I>
<L>データ7</L>
</H>
<M>データ8<N>データ9</N>
</M>
</G>
<O>データ10</O>
</row>
<!-- row: 2 -->
<row>
<A attrid="attrV">
<AA>データ11</AA>
<C id="text">データ12</C>
</A>
<D>
<E>データ13</E>
<F>データ14</F>
</D>
<G>
<H>
<I klm="post">
<J year="2024">データ15</J>
<K>データ16</K>
</I>
<L>データ17</L>
</H>
<M>データ18<N>データ19</N>
</M>
</G>
<O>データ20</O>
</row>
<!-- row: 3 -->
<row>
<A attrid="attrV">
<AA>21</AA>
<C id="text"></C>
</A>
<D>
<E>23<br>B</E>
<F>24</F>
</D>
<G>
<H>
<I klm="post">
<J year="2024"></J>
<K>26</K>
</I>
<L></L>
</H>
<M>28<N></N>
</M>
</G>
<O></O>
</row>
</all>