XMLで半角スペースが消える問題とは?原因と解決方法を徹底解説

プログラミング・IT

XMLファイルを扱っているとき、「入力した半角スペースが消えてしまう」「連続したスペースが1つにまとまってしまう」といった経験はありませんか?

これは実は、XMLの仕様による正常な動作なんです。でも、意図したスペースまで消えてしまうと困りますよね。特にプログラムでXMLを読み書きしたり、データを整形したりする場合、この問題は大きな障害になることがあります。

この記事では、XMLで半角スペースが消える理由から、具体的な対処法まで、初心者の方にもわかりやすく解説していきます。この問題を理解して、確実にスペースを保持する方法を身につけましょう。

スポンサーリンク

XMLで半角スペースが消える現象とは

よくある症状

XMLを扱う際に、以下のような問題が発生します:

パターン1:連続スペースが1つになる

<!-- 入力 -->
<text>これは    テストです</text>

<!-- 結果 -->
これは テストです

4つのスペースが1つになってしまいます。

パターン2:前後のスペースが削除される

<!-- 入力 -->
<text>  前後にスペース  </text>

<!-- 結果 -->
前後にスペース

前後のスペースが消えてしまうんです。

パターン3:改行とインデントのスペースが消える

<!-- 入力 -->
<message>
    こんにちは
    世界
</message>

<!-- 結果 -->
こんにちは 世界

改行とインデント用のスペースがすべて無視されます。

なぜ問題になるのか

影響を受ける場面:

  • テキストデータの整形情報が失われる
  • プログラムコードのインデントが崩れる
  • 詩や台本など、レイアウトが重要なコンテンツが壊れる
  • 住所や氏名などのデータで意図しない変換が起こる
  • データベースとの連携で不整合が発生

こういった問題を防ぐには、XMLの空白処理の仕組みを理解する必要があります。

半角スペースが消える原因

XMLの空白正規化(White Space Normalization)

XMLには「空白正規化」という仕様があります。これは、XMLパーサー(XMLを読み取るプログラム)が空白文字を自動的に処理する仕組みのこと。

空白文字とは:

  • 半角スペース(U+0020)
  • タブ文字(U+0009)
  • 改行(LF:U+000A)
  • キャリッジリターン(CR:U+000D)

これらの文字は、XMLの読み取り時に特別な扱いを受けるんです。

空白処理の3つのルール

XMLでは、要素の種類によって空白の扱い方が変わります。

ルール1:要素間の空白は無視される

<root>
    <child>内容</child>
</root>

タグとタグの間にあるインデント用の空白は、通常無視されます。

ルール2:要素内容の前後の空白は削除される

<text>  内容  </text>

要素の内容の前後にある空白は、自動的に削除されることがあります。

ルール3:連続する空白は1つにまとめられる

<text>連続    する    スペース</text>

連続したスペースは、1つのスペースに正規化されるんですね。

パーサーによる違い

XMLを読み取るプログラム(パーサー)によって、空白の扱い方が少し異なる場合があります。

DOM(Document Object Model)パーサー:
多くの場合、空白を正規化する

SAX(Simple API for XML)パーサー:
空白をそのまま渡すこともある

アプリケーション側での処理:
パーサーが渡した空白を、アプリケーションがさらに処理することもある

結果として、意図したスペースが消えてしまうわけです。

解決方法1:xml:space属性を使う

xml:space=”preserve”の基本

最も標準的な解決方法は、xml:space属性を使うことです。

基本的な使い方:

<text xml:space="preserve">これは    テストです</text>

xml:space="preserve"を指定すると、その要素内のすべての空白が保持されます。

適用範囲

親要素に指定した場合:

<root xml:space="preserve">
    <child>  前後にスペース  </child>
    <child>連続    する    スペース</child>
</root>

親要素に指定すると、子要素すべてに効果が及びます。ただし、子要素で明示的にxml:space="default"を指定すれば、その部分だけ標準動作に戻せますよ。

具体的な使用例

例1:詩やテキストアートの保存

<poem xml:space="preserve">
桜咲く
    春の日に
        君を想う
</poem>

インデントや改行がそのまま保持されます。

例2:プログラムコードの保存

<code xml:space="preserve">
function hello() {
    console.log("Hello, World!");
}
</code>

コードのインデントが崩れずに保存できるんです。

例3:整形済みテキスト

<pre xml:space="preserve">
名前      年齢    職業
田中太郎   30     エンジニア
鈴木花子   28     デザイナー
</pre>

表形式の整形が保持されます。

解決方法2:CDATAセクションを使う

CDATAとは

CDATA(Character Data)セクションは、XMLのマークアップとして解釈されない生のテキストデータを記述するための仕組みです。

構文:

<text><![CDATA[これは    テスト    です]]></text>

<![CDATA[]]> で囲まれた部分は、すべて文字データとして扱われます。

CDATAのメリット

特徴:

  • すべての空白が保持される
  • <>などの特殊文字をエスケープ不要
  • HTMLコードなども安全に格納できる

使用例:

<script><![CDATA[
function test() {
    if (a < b && c > d) {
        console.log("テスト    実行");
    }
}
]]></script>

JavaScriptコードの<>をエスケープせずに書けますし、スペースも保持されます。

CDATAの注意点

制限事項:

  • ]]>という文字列はCDATA内に書けない
  • 入れ子にはできない
  • XMLとして解析されないので、タグは使えない

例(不可能):

<text><![CDATA[これは]]>終了記号を含む]]></text>

]]>がCDATAセクションの終了と認識されてエラーになります。

解決方法3:文字実体参照を使う

ノーブレークスペースを使う

半角スペースの代わりに、「ノーブレークスペース」という特殊な空白文字を使う方法もあります。

記述方法:

<text>これは&#160;&#160;&#160;&#160;テストです</text>

&#160;は、改行されない空白(ノーブレークスペース)を表す文字実体参照です。

または:

<text>これは&nbsp;&nbsp;&nbsp;&nbsp;テストです</text>

HTMLでおなじみの&nbsp;も使えます(ただし、XMLで使う場合は事前に定義が必要)。

他の空白文字実体参照

よく使われる空白文字:

  • &#32; – 通常の半角スペース(U+0020)
  • &#160; – ノーブレークスペース(U+00A0)
  • &#8194; – enスペース(U+2002)
  • &#8195; – emスペース(U+2003)
  • &#9; – タブ文字(U+0009)

使用例:

<text>値1&#9;値2&#9;値3</text>

タブで区切られたデータを明示的に表現できます。

この方法の利点と欠点

利点:

  • xml:space属性なしでスペースを保持できる
  • 細かい制御が可能

欠点:

  • 可読性が低い
  • 編集が面倒
  • データ量が増える

解決方法4:プログラムでの処理

XMLを読み込む際の設定

プログラミング言語でXMLを扱う場合、パーサーの設定で空白処理を制御できます。

Python(xml.etree.ElementTree):

import xml.etree.ElementTree as ET

# XMLを解析
tree = ET.parse('file.xml')
root = tree.getroot()

# テキストを取得(空白は正規化される可能性がある)
text = root.find('text').text

# xml:space属性を確認
if root.find('text').get('{http://www.w3.org/XML/1998/namespace}space') == 'preserve':
    # 空白を保持する処理
    pass

Java(DOM Parser):

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringElementContentWhitespace(false); // 空白を無視しない
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("file.xml");

setIgnoringElementContentWhitespace(false)で、要素間の空白を保持できます。

XMLを書き出す際の設定

Python:

import xml.etree.ElementTree as ET

root = ET.Element('root')
text_elem = ET.SubElement(root, 'text')
text_elem.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
text_elem.text = 'これは    テスト    です'

tree = ET.ElementTree(root)
tree.write('output.xml', encoding='utf-8', xml_declaration=True)

プログラムでxml:space属性を追加して書き出すこともできますよ。

C#(.NET)での処理

XMLの読み込み:

XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true; // 空白を保持
doc.Load("file.xml");

XMLの書き出し:

XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "  ";

using (XmlWriter writer = XmlWriter.Create("output.xml", settings))
{
    writer.WriteStartElement("text");
    writer.WriteAttributeString("xml", "space", null, "preserve");
    writer.WriteString("これは    テスト    です");
    writer.WriteEndElement();
}

実践的なケーススタディ

ケース1:住所データの保存

住所には全角・半角スペースが混在することがあります。

問題のある例:

<address>東京都  渋谷区  神南  1-1-1</address>

複数のスペースが1つにまとめられてしまいます。

解決策:

<address xml:space="preserve">東京都  渋谷区  神南  1-1-1</address>

または:

<address><![CDATA[東京都  渋谷区  神南  1-1-1]]></address>

ケース2:HTMLコードの保存

HTMLコードをXML内に保存する場合、CDATAが便利です。

解決策:

<html-snippet><![CDATA[
<div class="container">
    <p>これは  サンプル  です</p>
</div>
]]></html-snippet>

HTMLのタグも、スペースも、すべてそのまま保存できるんです。

ケース3:設定ファイルでの値の保持

設定ファイルで、意図的に前後にスペースを含む値を保存したい場合があります。

解決策:

<config>
    <padding xml:space="preserve">  10px  </padding>
    <margin xml:space="preserve"> 5px </margin>
</config>

ケース4:データベースのデータ移行

データベースから出力したデータをXMLで保存する場合、元のスペースを保持する必要があります。

解決策:

<records>
    <record>
        <name xml:space="preserve">田中  太郎</name>
        <description xml:space="preserve">
これは
複数行の
説明文です
        </description>
    </record>
</records>

XMLスキーマでの空白制御

XSD(XML Schema)での定義

XMLスキーマを使う場合、要素の空白処理方法を定義できます。

スキーマ定義例:

<xs:element name="text">
    <xs:simpleType>
        <xs:restriction base="xs:string">
            <xs:whiteSpace value="preserve"/>
        </xs:restriction>
    </xs:simpleType>
</xs:element>

whiteSpace属性の値:

  • preserve – すべての空白を保持
  • replace – タブ、改行、キャリッジリターンをスペースに置き換え
  • collapse – replaceに加えて、連続スペースを1つにまとめ、前後のスペースを削除

スキーマを活用した統一的な処理

組織やプロジェクトでXMLを扱う場合、スキーマで空白処理のルールを統一しておくと、混乱を防げます。

例:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="code">
        <xs:complexType>
            <xs:simpleContent>
                <xs:extension base="xs:string">
                    <xs:attribute name="space" fixed="preserve"/>
                </xs:extension>
            </xs:simpleContent>
        </xs:complexType>
    </xs:element>
</xs:schema>

トラブルシューティング

xml:space属性が効かない場合

確認ポイント:

  1. 属性名のスペルは正しいか(xml:space
  2. 値はpreserveになっているか
  3. 使用しているパーサーが対応しているか
  4. アプリケーション側で空白を削除していないか

デバッグ方法:
XMLファイルを直接開いて、ソースコードを確認しましょう。それでも問題がある場合は、パーサーやアプリケーションの設定を見直す必要があります。

CDATAセクションが表示される

CDATAセクションのマーカー(<![CDATA[]]>)がそのまま表示される場合、XMLとして正しく解析されていません。

原因:

  • ファイルの拡張子が.xmlでない
  • Content-Typeが正しくない
  • テキストエディタで開いている

正しいXMLパーサーを使って読み込みましょう。

プログラムで読み込むと消える

パーサーやアプリケーションの設定を確認してください。

チェック項目:

  • パーサーの空白処理設定
  • trim()などの文字列処理関数を使っていないか
  • テンプレートエンジンが空白を削除していないか

意図しない場所で空白が削除されている可能性があります。

ベストプラクティス

用途に応じた方法の選択

シンプルなテキスト:
xml:space="preserve"を使用

コードやHTMLを含む:
CDATAセクションを使用

プログラムでの自動生成:
パーサーの設定で制御

厳密なデータ管理:
XMLスキーマで定義

目的に応じて、最適な方法を選びましょう。

一貫性のある命名規則

XML要素名で、空白を保持する要素であることを示すのも良い方法です。

例:

<formatted-text xml:space="preserve">...</formatted-text>
<plain-text>...</plain-text>
<code xml:space="preserve">...</code>

要素名から、空白の扱い方が推測できるようにするんですね。

ドキュメント化

XMLファイルやスキーマに、空白処理の方針をコメントで記載しておくと、後から見た人にもわかりやすくなります。

例:

<!-- この要素ではxml:space="preserve"を使用して空白を保持しています -->
<description xml:space="preserve">
詳細な説明文...
</description>

まとめ

XMLで半角スペースが消える問題は、XMLの仕様による空白正規化が原因です。

この記事のポイント:

  • XMLは空白を自動的に正規化する仕様がある
  • 連続スペースは1つにまとめられ、前後のスペースは削除される
  • xml:space=”preserve”属性で空白を保持できる
  • CDATAセクションでも空白をそのまま保存可能
  • 文字実体参照(&#160;など)を使う方法もある
  • プログラムではパーサーの設定で制御できる
  • XMLスキーマで空白処理のルールを定義できる
  • 用途に応じて最適な方法を選ぶことが重要

この問題を理解していないと、データの不整合やレイアウト崩れなど、予期しないトラブルに遭遇することがあります。でも、適切な対処法を知っていれば、確実にスペースを保持して、意図した通りのXMLを作成できるんです。

XMLを扱う際は、空白処理の仕様を意識して、必要に応じてxml:space属性やCDATAセクションを活用しましょう。そうすることで、データの整合性を保ちながら、安全にXMLを扱えるようになりますよ。

コメント

タイトルとURLをコピーしました