XMLでリストを表現する方法|配列データの書き方を完全解説

プログラミング・IT

プログラミングをしていると、複数のデータをまとめて扱う「リスト(配列)」を使う場面が頻繁にありますよね。

JSONなら[]で簡単に配列を表現できますが、XMLではどうやってリストを書けばいいの? と疑問に思った方も多いはず。実は、XMLにはリストを表現する決まった方法がないため、いくつかのパターンが存在するんです。

この記事では、XMLでリストやコレクションを表現する複数の方法を、それぞれのメリット・デメリットとともに詳しく解説します。実際のコード例も豊富に紹介しますので、XMLデータの設計に迷っている方は、ぜひ参考にしてください!


スポンサーリンク

XMLにおけるリストの基本概念

まず、XMLでリストを扱う際の基本的な考え方を理解しましょう。

JSONとXMLの違い

JSONの場合:

{
  "users": [
    {"name": "田中太郎", "age": 30},
    {"name": "佐藤花子", "age": 25},
    {"name": "鈴木次郎", "age": 35}
  ]
}

配列は[]で明確に表現されていますね。

XMLの場合:
XMLには配列を表す特別な構文がありません。代わりに、要素の繰り返しでリストを表現します。

<users>
  <user>
    <name>田中太郎</name>
    <age>30</age>
  </user>
  <user>
    <name>佐藤花子</name>
    <age>25</age>
  </user>
  <user>
    <name>鈴木次郎</name>
    <age>35</age>
  </user>
</users>

XMLでリストを表現する考え方

XMLでは、同じ名前の要素を繰り返すことで、リストを表現します。

重要なのは:

  • コンテナ要素(親要素)でグループ化する
  • 各アイテム要素(子要素)を繰り返す
  • 命名規則で意図を明確にする

パターン1:複数形/単数形の親子構造(推奨)

最も一般的で推奨される方法です。

基本的な構造

<users>
  <user>
    <name>田中太郎</name>
    <email>tanaka@example.com</email>
  </user>
  <user>
    <name>佐藤花子</name>
    <email>sato@example.com</email>
  </user>
</users>

構造の説明:

  • <users>:複数形の親要素(コンテナ)
  • <user>:単数形の子要素(アイテム)
  • 繰り返される<user>がリストの各要素

なぜ推奨されるのか

1. 意味が明確
親要素が複数形、子要素が単数形なので、「これはリストだ」と一目で分かります。

2. 構造が美しい
階層構造が整然としていて、人間にもパーサーにも読みやすいです。

3. 拡張性がある
将来、リスト全体に属性を追加したい時も対応しやすいです。

<users total="3" page="1">
  <user id="1">
    <name>田中太郎</name>
  </user>
  <user id="2">
    <name>佐藤花子</name>
  </user>
</users>

実用的な例

商品リスト:

<products>
  <product>
    <id>P001</id>
    <name>ノートパソコン</name>
    <price>89800</price>
    <stock>15</stock>
  </product>
  <product>
    <id>P002</id>
    <name>ワイヤレスマウス</name>
    <price>2980</price>
    <stock>50</stock>
  </product>
  <product>
    <id>P003</id>
    <name>USB-Cケーブル</name>
    <price>1280</price>
    <stock>100</stock>
  </product>
</products>

注文履歴:

<orders>
  <order>
    <orderId>ORD-2025-001</orderId>
    <customer>田中太郎</customer>
    <date>2025-01-15</date>
    <items>
      <item>
        <productId>P001</item>
        <quantity>1</quantity>
      </item>
      <item>
        <productId>P002</item>
        <quantity>2</quantity>
      </item>
    </items>
  </order>
</orders>

ネストしたリスト(リストの中のリスト)も、同じパターンで表現できます。


パターン2:親要素なしの直接繰り返し

親要素を省略して、要素を直接繰り返す方法もあります。

構造例

<root>
  <item>値1</item>
  <item>値2</item>
  <item>値3</item>
</root>

または、ルート要素で直接繰り返す場合もあります(ただし、整形式XMLでは1つのルート要素が必要):

<items>
  <item>値1</item>
  <item>値2</item>
  <item>値3</item>
</items>

メリット

シンプル
階層が浅くなり、XML全体がコンパクトになります。

パース処理が簡単
親要素を気にせず、直接アイテム要素を取得できます。

デメリット

リスト全体への属性付与が困難
コンテナ要素がないため、リスト全体のメタ情報を持たせにくいです。

複数のリストが混在すると混乱

<data>
  <user>太郎</user>
  <user>花子</user>
  <product>商品A</product>
  <product>商品B</product>
</data>

どれがどのリストに属するのか分かりにくくなります。

使いどころ

  • シンプルな文字列のリスト
  • リスト全体のメタ情報が不要な場合
  • 単一のリストしか含まないXML

例:タグリスト

<tags>
  <tag>プログラミング</tag>
  <tag>XML</tag>
  <tag>データ構造</tag>
</tags>

パターン3:属性でリスト要素を区別

属性を使って、各アイテムを識別する方法です。

構造例

<users>
  <user id="1" name="田中太郎" age="30" email="tanaka@example.com"/>
  <user id="2" name="佐藤花子" age="25" email="sato@example.com"/>
  <user id="3" name="鈴木次郎" age="35" email="suzuki@example.com"/>
</users>

すべての情報を属性に詰め込むパターンです。

メリット

コンパクト
1行で表現できるため、XMLファイルが短くなります。

軽量
テキストサイズが小さくなり、通信量が削減できます。

デメリット

可読性が低い
属性が多くなると、人間には読みにくくなります。

ネスト構造が作れない
属性は平坦なので、複雑なデータ構造には向きません。

特殊文字の扱いが面倒
属性値には特殊文字のエスケープが必要です。

<!-- 悪い例:属性値に " が含まれる -->
<item description="これは&quot;便利な&quot;商品です"/>

使いどころ

  • データがシンプルで平坦な場合
  • ファイルサイズを極力小さくしたい場合
  • 設定ファイルなど、機械的に処理されるデータ

パターン4:混合型(要素と属性の併用)

要素と属性を組み合わせる、実用的なアプローチです。

構造例

<users>
  <user id="1">
    <name>田中太郎</name>
    <email>tanaka@example.com</email>
    <profile>
      <age>30</age>
      <department>開発部</department>
    </profile>
  </user>
  <user id="2">
    <name>佐藤花子</name>
    <email>sato@example.com</email>
    <profile>
      <age>25</age>
      <department>営業部</department>
    </profile>
  </user>
</users>

使い分けのルール:

  • 属性:ID、種別、状態など、メタデータ
  • 要素:実際の内容、複雑なデータ

メリット

柔軟性が高い
それぞれの特性を活かした設計ができます。

可読性と効率のバランス
重要な情報は要素で詳しく、補助情報は属性でコンパクトに。

実用例

商品カタログ:

<products category="electronics">
  <product id="P001" status="available">
    <name>ノートパソコン</name>
    <description>高性能な15インチモデル</description>
    <price currency="JPY">89800</price>
    <specifications>
      <cpu>Intel Core i7</cpu>
      <memory>16GB</memory>
      <storage>512GB SSD</storage>
    </specifications>
  </product>
</products>

ブログ記事:

<posts>
  <post id="123" published="true" featured="false">
    <title>XMLでリストを扱う方法</title>
    <author>山田太郎</author>
    <publishedDate>2025-01-15</publishedDate>
    <content>
      <![CDATA[記事の本文がここに入ります...]]>
    </content>
    <tags>
      <tag>XML</tag>
      <tag>プログラミング</tag>
    </tags>
  </post>
</posts>

空のリストの表現方法

リストに要素が1つもない場合、どう書くべきでしょうか。

方法1:空の親要素

<users>
  <!-- アイテムなし -->
</users>

または:

<users/>

最もシンプルで推奨される方法です。

方法2:明示的に空を示す

<users count="0">
  <!-- ユーザーなし -->
</users>

属性で「空である」ことを明示する方法。

方法3:nullやemptyを使う

<users>
  <empty/>
</users>

または:

<users null="true"/>

あまり推奨されませんが、このような方法もあります。

推奨

空の親要素(方法1)がベストです。XMLパーサーは空要素を自然に扱えますし、コードもシンプルになります。


各プログラミング言語でのXMLリストのパース

実際にコードでどう扱うか見ていきましょう。

Pythonでのパース

import xml.etree.ElementTree as ET

xml_string = '''
<users>
  <user>
    <name>田中太郎</name>
    <age>30</age>
  </user>
  <user>
    <name>佐藤花子</name>
    <age>25</age>
  </user>
</users>
'''

root = ET.fromstring(xml_string)

# すべてのuserユーザー要素を取得
users = root.findall('user')

for user in users:
    name = user.find('name').text
    age = user.find('age').text
    print(f'{name}: {age}歳')

# 出力:
# 田中太郎: 30歳
# 佐藤花子: 25歳

リスト化する:

user_list = []
for user in root.findall('user'):
    user_dict = {
        'name': user.find('name').text,
        'age': int(user.find('age').text)
    }
    user_list.append(user_dict)

print(user_list)
# [{'name': '田中太郎', 'age': 30}, {'name': '佐藤花子', 'age': 25}]

JavaScriptでのパース

const xmlString = `
<users>
  <user>
    <name>田中太郎</name>
    <age>30</age>
  </user>
  <user>
    <name>佐藤花子</name>
    <age>25</age>
  </user>
</users>
`;

const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, "text/xml");

// すべてのuser要素を取得
const users = xmlDoc.getElementsByTagName('user');

// 配列に変換
const userList = Array.from(users).map(user => {
  return {
    name: user.getElementsByTagName('name')[0].textContent,
    age: parseInt(user.getElementsByTagName('age')[0].textContent)
  };
});

console.log(userList);
// [{name: '田中太郎', age: 30}, {name: '佐藤花子', age: 25}]

Javaでのパース

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.StringReader;
import org.xml.sax.InputSource;
import java.util.ArrayList;
import java.util.List;

public class XMLListParser {
    public static void main(String[] args) throws Exception {
        String xmlString = """
            <users>
              <user>
                <name>田中太郎</name>
                <age>30</age>
              </user>
              <user>
                <name>佐藤花子</name>
                <age>25</age>
              </user>
            </users>
            """;

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(
            new InputSource(new StringReader(xmlString))
        );

        // すべてのuser要素を取得
        NodeList userNodes = doc.getElementsByTagName("user");

        List<User> userList = new ArrayList<>();

        for (int i = 0; i < userNodes.getLength(); i++) {
            Element userElement = (Element) userNodes.item(i);

            String name = userElement
                .getElementsByTagName("name")
                .item(0)
                .getTextContent();

            int age = Integer.parseInt(
                userElement
                    .getElementsByTagName("age")
                    .item(0)
                    .getTextContent()
            );

            userList.add(new User(name, age));
        }

        userList.forEach(System.out::println);
    }
}

class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + ": " + age + "歳";
    }
}

PHPでのパース

<?php
$xmlString = <<<XML
<users>
  <user>
    <name>田中太郎</name>
    <age>30</age>
  </user>
  <user>
    <name>佐藤花子</name>
    <age>25</age>
  </user>
</users>
XML;

$xml = simplexml_load_string($xmlString);

$userList = [];

foreach ($xml->user as $user) {
    $userList[] = [
        'name' => (string)$user->name,
        'age' => (int)$user->age
    ];
}

print_r($userList);

/*
Array
(
    [0] => Array
        (
            [name] => 田中太郎
            [age] => 30
        )
    [1] => Array
        (
            [name] => 佐藤花子
            [age] => 25
        )
)
*/
?>

XMLリストのベストプラクティス

実践で使える設計のコツをまとめます。

1. 一貫性のある命名規則

推奨:複数形/単数形のペア

<users>
  <user>...</user>
</users>

<products>
  <product>...</product>
</products>

<orders>
  <order>...</order>
</orders>

避けるべき:不規則な命名

<!-- 悪い例 -->
<userList>
  <userItem>...</userItem>
</userList>

<productCollection>
  <productData>...</productData>
</productCollection>

2. メタデータは親要素に

リスト全体の情報は、親要素の属性に配置します。

<users total="100" page="1" pageSize="10">
  <user id="1">
    <name>田中太郎</name>
  </user>
  <!-- ... -->
</users>

3. IDは属性、内容は要素

識別子やメタ情報は属性、実際のデータは要素に。

<products category="electronics">
  <product id="P001" status="active">
    <name>商品名</name>
    <description>商品説明</description>
    <price>10000</price>
  </product>
</products>

4. 深すぎる階層を避ける

階層が深くなりすぎると、パースも複雑になります。

悪い例(深すぎる):

<data>
  <items>
    <itemList>
      <itemCollection>
        <item>
          <itemData>
            <itemInfo>
              <name>商品名</name>
            </itemInfo>
          </itemData>
        </item>
      </itemCollection>
    </itemList>
  </items>
</data>

良い例(シンプル):

<items>
  <item>
    <name>商品名</name>
  </item>
</items>

5. 名前空間を活用する

複数のリストタイプが混在する場合、名前空間で区別します。

<catalog xmlns:prod="http://example.com/products"
         xmlns:cat="http://example.com/categories">

  <cat:categories>
    <cat:category id="1">電化製品</cat:category>
  </cat:categories>

  <prod:products>
    <prod:product categoryId="1">
      <prod:name>ノートパソコン</prod:name>
    </prod:product>
  </prod:products>

</catalog>

XMLとJSONの比較

リスト表現において、XMLとJSONの違いを見てみましょう。

同じデータの表現

XML:

<users>
  <user>
    <id>1</id>
    <name>田中太郎</name>
    <emails>
      <email type="work">tanaka@work.com</email>
      <email type="personal">tanaka@gmail.com</email>
    </emails>
  </user>
  <user>
    <id>2</id>
    <name>佐藤花子</name>
    <emails>
      <email type="work">sato@work.com</email>
    </emails>
  </user>
</users>

JSON:

{
  "users": [
    {
      "id": 1,
      "name": "田中太郎",
      "emails": [
        {"type": "work", "address": "tanaka@work.com"},
        {"type": "personal", "address": "tanaka@gmail.com"}
      ]
    },
    {
      "id": 2,
      "name": "佐藤花子",
      "emails": [
        {"type": "work", "address": "sato@work.com"}
      ]
    }
  ]
}

それぞれの特徴

特徴XMLJSON
リストの明示性要素の繰り返し[]で明確
ファイルサイズ大きめ小さめ
可読性冗長だが構造的シンプル
型情報すべて文字列数値、真偽値を区別
メタデータ属性で柔軟限定的
パース速度やや遅い高速

どちらを選ぶべきか

XMLが向いているケース:

  • 文書構造が重要(論文、マニュアルなど)
  • メタデータが豊富に必要
  • レガシーシステムとの互換性
  • SOAP APIなどの既存標準

JSONが向いているケース:

  • Web APIでのデータ交換
  • JavaScriptとの連携
  • シンプルなデータ構造
  • 軽量・高速が優先

よくある問題とトラブルシューティング

XMLリストで遭遇しやすい問題と解決策です。

問題1:単一要素の時に配列にならない

多くのXMLパーサーで起こる問題です。

XML:

<users>
  <user>
    <name>田中太郎</name>
  </user>
</users>

JavaScript(一部のライブラリ):

// 期待:配列になってほしい
// 実際:オブジェクトになる
{
  users: {
    user: {name: '田中太郎'}  // 配列ではない!
  }
}

解決策:
パース後に必ず配列に変換する処理を入れる。

function ensureArray(value) {
  return Array.isArray(value) ? value : [value];
}

const users = ensureArray(parsedXML.users.user);

問題2:空リストの扱い

空のリストをどう解釈するか、パーサーによって異なります。

<users>
  <!-- 空 -->
</users>

対処法:
コードで明示的にチェックする。

users = root.findall('user')
if not users:
    print('ユーザーなし')
else:
    for user in users:
        # 処理

問題3:属性と要素の混在で混乱

<user id="1" name="田中太郎">
  <email>tanaka@example.com</email>
</user>

パースする際、属性と要素を別々に取得する必要があります。

Python:

user = root.find('user')
user_id = user.attrib['id']  # 属性
name = user.attrib['name']    # 属性
email = user.find('email').text  # 要素

XMLスキーマでリストを定義する

XMLスキーマ(XSD)を使って、リストの構造を定義できます。

基本的なリストの定義

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <!-- usersリストの定義 -->
  <xs:element name="users">
    <xs:complexType>
      <xs:sequence>
        <!-- user要素が0回以上繰り返される -->
        <xs:element name="user" minOccurs="0" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="name" type="xs:string"/>
              <xs:element name="age" type="xs:integer"/>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

重要な属性:

  • minOccurs="0":最小0個(空リストOK)
  • maxOccurs="unbounded":上限なし(無制限に繰り返せる)

固定長リストの定義

<!-- 必ず3つの要素を持つリスト -->
<xs:element name="items">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="item" type="xs:string" 
                  minOccurs="3" maxOccurs="3"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

まとめ:XMLリストの設計指針

XMLでリストを表現する方法をまとめます。

基本パターン:

1. 複数形/単数形の親子構造(最推奨)

<users>
  <user>...</user>
  <user>...</user>
</users>

2. 直接繰り返し(シンプルなリスト向け)

<items>
  <item>値1</item>
  <item>値2</item>
</items>

3. 混合型(実用的)

<users total="2">
  <user id="1">
    <name>太郎</name>
  </user>
</users>

設計のベストプラクティス:

  • 一貫性のある命名規則を使う
  • メタデータは親要素の属性に
  • IDや種別は属性、内容は要素に
  • 階層を深くしすぎない
  • 空リストは空の親要素で表現

パース時の注意点:

  • 単一要素の時の配列化処理
  • 空リストのチェック
  • 属性と要素の区別
  • 言語・ライブラリごとの違いを把握

XMLとJSONの使い分け:

  • 文書構造・メタデータ重視 → XML
  • シンプル・軽量・Web API → JSON

XMLでリストを扱う際は、「配列の明示的な構文がない」という特性を理解した上で、読みやすく保守しやすい構造を心がけましょう。この記事で紹介したパターンを参考に、あなたのプロジェクトに最適な設計を選んでください!


関連キーワード: XML、リスト、配列、コレクション、要素の繰り返し、親子構造、XMLスキーマ、XSD、JSON比較、パース、複数形単数形、データ構造、階層構造、ベストプラクティス

コメント

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