はじめに
XML(eXtensible Markup Language)は、構造化されたデータの表現に特化したマークアップ言語であり、テキストベースの形式で情報を記述する。拡張可能なタグを使用することで、様々な分野でのデータ交換や保存に広く利用されている。XMLはSGML(Standard Generalized Markup Language)を簡略化したものである。
XMLの基本
XMLのデータセットは、文書(ドキュメント)という単位で扱われる。ドキュメントは一般的にはファイルで表され、その拡張子は.xmlである。
XMLの基本構造は以下のようになっている。
<親要素>
<子要素1>テキスト内容</子要素1>
<子要素2 属性="値">テキスト内容</子要素2>
</親要素>
- 要素(Element)
- 開始タグ
<要素名>と終了タグ</要素名>で囲まれたデータのコンテナ 例: <要素名>内容</要素名>
- 空要素(内容のない要素)は
/で終了 例: <空要素 />
- 属性(Attribute)
- 要素に追加情報を提供する名前=値のペア 例:
<要素 属性1="値1" 属性2="値2">
- 属性値は常に引用符(単一または二重)で囲む
- テキスト内容(Text Content)
- 要素内のデータ 例:
<要素>テキスト内容</要素>
タグ名や属性名には大文字小文字の区別がある。要素は正しく閉じる必要がある。要素は入れ子にできるが、正しく入れ子にする必要がある(<親><子></子></親>はよいが、<親><子></親></子>はだめ)。
以下のようにコメントを入れることができる。
見ての通り、HTMLそっくりだが、HTMLもSGML由来であり、XMLの兄弟なのである。
XMLはプロセッサ(パーサー)によって処理されるが、それに対して指示を与える方法が定義されている。
<?xml-stylesheet type="text/xsl" href="style.xsl"?>
ドキュメントの最初にはXML宣言が必要である。
<?xml version="1.0" encoding="UTF-8"?>
以下は具体的なXMLドキュメントの例である。
<?xml version="1.0" encoding="UTF-8"?>
<書籍>
<タイトル>XML入門</タイトル>
<著者>
<名前>山田太郎</名前>
<国籍>日本</国籍>
</著者>
<章 番号="1">
<章タイトル>XMLの基本</章タイトル>
</章>
<章 番号="2">
<章タイトル>XMLの応用</章タイトル>
</章>
</書籍>
HTMLから見ると、XMLは独自のタグでデータの構造を記述するかたちになっている。
XMLの記述
DTD
文書型定義(Document Type Definition, DTD)は、XMLドキュメントの構造と内容を定義するための仕組みである。DTDを使用することで、XMLドキュメントの有効性(文法的に問題ないか)と正当性(内容的に問題ないか)を検証できる。
DTDは、XMLドキュメントにおいて次のように記述する。
<!DOCTYPE 書籍 [
<!-- 要素の定義 -->
<!ELEMENT 書籍 (タイトル, 著者, 章+)>
<!ELEMENT タイトル (#PCDATA)>
<!ELEMENT 著者 (名前, 国籍)>
<!ELEMENT 名前 (#PCDATA)>
<!ELEMENT 国籍 (#PCDATA)>
<!ELEMENT 章 (章タイトル)>
<!ELEMENT 章タイトル (#PCDATA)>
<!-- 属性の定義 -->
<!ATTLIST 章
番号 CDATA #REQUIRED
発行日 CDATA #IMPLIED
>
]>
あるいは、外部ファイルとして指定することもできる。
<!DOCTYPE 書籍 SYSTEM "book.dtd">
ファイルの中身は次のようになる。
<!-- book.dtd -->
<!-- 要素の定義 -->
<!ELEMENT 書籍 (タイトル, 著者, 章+)>
<!ELEMENT タイトル (#PCDATA)>
<!ELEMENT 著者 (名前, 国籍)>
<!ELEMENT 名前 (#PCDATA)>
<!ELEMENT 国籍 (#PCDATA)>
<!ELEMENT 章 (章タイトル)>
<!ELEMENT 章タイトル (#PCDATA)>
<!-- 属性の定義 -->
<!ATTLIST 章
番号 CDATA #REQUIRED
発行日 CDATA #IMPLIED
>
- 要素の定義
- 要素名と内容モデルを定義
- 内容モデルは子要素のリストやテキスト内容を指定 例:
<!ELEMENT 書籍 (タイトル, 著者, 章+)>
- 属性の定義
- 要素の属性名、型、デフォルト値を定義 例:
<!ATTLIST 章 番号 CDATA #REQUIRED>
ここで、要素定義(ELEMENT)の括弧の中身を内容モデルという。#PCDATAというのは、パーサーに解釈される文字データの意味である。章+といというのは、章が1回以上出てくるという意味である。属性定義(ATTLIST)のCDATAはパーサーに解釈されない文字データの意味である。#REQUIREDは必須属性、#IMPLIEDは省略可能な属性の意味である。
XSD
XMLスキーマ(XSD: XML Schema Definition)は、XMLドキュメントの構造、データ型、制約を定義するための強力な仕組みであり、DTDに比べてより柔軟で詳細な定義が可能である。
<!-- book.xsd -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- 書籍型の定義 -->
<xs:complexType name="書籍型">
<xs:sequence>
<!-- タイトル要素 -->
<xs:element name="タイトル" type="xs:string"/>
<!-- 著者要素 -->
<xs:element name="著者" type="著者型"/>
<!-- 章要素(1回以上繰り返し) -->
<xs:element name="章" type="章型" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- 著者型の定義 -->
<xs:complexType name="著者型">
<xs:sequence>
<xs:element name="名前" type="xs:string"/>
<xs:element name="国籍" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<!-- 章型の定義 -->
<xs:complexType name="章型">
<xs:sequence>
<xs:element name="章タイトル" type="xs:string"/>
</xs:sequence>
<!-- 属性の定義 -->
<xs:attribute name="番号" type="xs:string" use="required"/>
<xs:attribute name="発行日" type="xs:string" use="optional"/>
</xs:complexType>
<!-- 書籍要素の定義(書籍型を参照) -->
<xs:element name="書籍" type="書籍型"/>
</xs:schema>
XMLドキュメントでは以下のように参照する。
<書籍 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="book.xsd">
<タイトル>XML入門</タイトル>
...
</書籍>
名前空間
名前空間(Namespace)は、XMLやXSDなどのドキュメントで要素や属性の名前を一意に識別するための仕組みである。名前空間を使用することで、異なるドキュメントやスキーマ間で名前の衝突を防ぎ、複数のスキーマを組み合わせて使用することができる。
<!-- book_namespace.xsd -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/book"
xmlns:book="http://example.com/book"
elementFormDefault="qualified">
<!-- 書籍型の定義 -->
<xs:complexType name="書籍型">
<xs:sequence>
<!-- タイトル要素 -->
<xs:element name="タイトル" type="xs:string"/>
<!-- 著者要素 -->
<xs:element name="著者" type="book:著者型"/>
<!-- 章要素(1回以上繰り返し) -->
<xs:element name="章" type="book:章型" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- 著者型の定義 -->
<xs:complexType name="著者型">
<xs:sequence>
<xs:element name="名前" type="xs:string"/>
<xs:element name="国籍" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<!-- 章型の定義 -->
<xs:complexType name="章型">
<xs:sequence>
<xs:element name="章タイトル" type="xs:string"/>
</xs:sequence>
<!-- 属性の定義 -->
<xs:attribute name="番号" type="xs:string" use="required"/>
<xs:attribute name="発行日" type="xs:string" use="optional"/>
</xs:complexType>
<!-- 書籍要素の定義(書籍型を参照) -->
<xs:element name="書籍" type="book:書籍型"/>
</xs:schema>
ここでは、名前空間をhttp://example.com/bookとして、プレフィックスbookで参照するようにしている。elementFormDefault="qualified"というのがあるが、これによって子要素も同じ名前空間に入れている。XMLドキュメントは以下のようになる。
<?xml version="1.0" encoding="UTF-8"?>
<book:書籍 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://example.com/book book_namespace.xsd"
xmlns:book="http://example.com/book">
<book:タイトル>XML入門</book:タイトル>
<book:著者>
<book:名前>山田太郎</book:名前>
<book:国籍>日本</book:国籍>
</book:著者>
<book:章 番号="1">
<book:章タイトル>XMLの基本</book:章タイトル>
</book:章>
<book:章 番号="2">
<book:章タイトル>XMLの応用</book:章タイトル>
</book:章>
</book:書籍>
名前空間の名前は、URIである必要はないが、一意性の確保のためにURI形式が推奨されている。ただの識別子なので、URIにアクセスできる必要はない。
XMLの実践的な活用
XMLの検証
XMLの検証にはxmllintなどを使う。
$ sudo apt install libxml2-utils
DTDで検証する場合は、次のようにする。
$ xmllint --noout --dtdvalid book.dtd book.xml
XSDで検証する場合は、次のようにする。
$ xmllint --noout --schema book.xsd book.xml
Pythonで検証することもできる。
# validation.py
from lxml import etree
xsd = etree.XMLSchema(file="book.xsd")
xml = etree.parse("book.xml")
if xsd.validate(xml):
print("検証成功")
else:
print("検証失敗:", xsd.error_log
実行
プログラムでの利用
Pythonで処理するには、lxmlなどを使う。以下は、タグを表示する例である。
from lxml import etree
xml = etree.parse("book.xml")
root = xml.getroot()
# ルート要素のタグ名を表示
print("ルート要素のタグ名:", root.tag)
# 子要素のタグ名を表示
for child in root:
print("子要素のタグ名:", child.tag
子孫要素を巡回するには次のようにする。
# 要素の子孫要素を反復処理
for element in root.iter():
print("タグ名:", element.tag)
print("テキスト:", element.text)
print("属性:", element.attrib)
print("---")
XPathで検索することもできる。
# タイトル要素のテキストを表示
title = root.find(".//タイトル")
print("タイトルのテキスト:", title.text)
# 著者名要素のテキストを表示
author_name = root.find(".//名前")
print("著者名のテキスト:", author_name.text)
# 章要素の属性を表示
chapters = root.findall(".//章")
for chapter in chapters:
print("章の属性:", chapter.attrib)
print("章番号:", chapter.get("番号")
# xpathメソッドを使用して要素を選択
title = root.xpath("//書籍/タイトル")[0]
print("タイトル:", title.text)
author_name = root.xpath("//書籍/著者/名前")[0]
print("著者名:", author_name.text)
chapter = root.xpath("//書籍/章[@番号='1']")[0]
print("章タイトル:", chapter.find("章タイトル").text
XPath(XML Path Language)は、XMLドキュメント内の要素や属性を検索するための言語である。XPathでは、パス表現を使用して要素や属性を選択する。パス表現はスラッシュ(/)で区切られた要素名のリストで構成される。
//書籍/タイトル
@で属性を選択できる。
//書籍/章[@番号='1']
orなどの演算子も使える。
//書籍/章[@番号='1' or @番号='2']
JSONとの変換
XMLをJSONに変換するには、Pythonのxmltodictを使う。
XMLからJSONへの変換
import xmltodict
import json
# XMLファイルのパスを指定
xml_file_path = 'input.xml'
# XMLファイルを読み込む
with open(xml_file_path, 'r', encoding='utf-8') as xml_file:
xml_data = xml_file.read()
# XMLを辞書に変換
data_dict = xmltodict.parse(xml_data)
# 辞書をJSONに変換
json_data = json.dumps(data_dict, indent=4, ensure_ascii=False)
# JSONをファイルに書き込む
with open('output.json', 'w', encoding='utf-8') as json_file:
json_file.write(json_data)
print("XMLをJSONに変換しました。")
JSONからXMLへの変換
import xmltodict
import json
# JSONファイルのパスを指定
json_file_path = 'input.json'
# JSONファイルを読み込む
with open(json_file_path, 'r', encoding='utf-8') as json_file:
json_data = json_file.read()
# JSONを辞書に変換
data_dict = json.loads(json_data)
# 辞書をXMLに変換
xml_data = xmltodict.unparse(data_dict, pretty=True)
# XMLをファイルに書き込む
with open('output.xml', 'w', encoding='utf-8') as xml_file:
xml_file.write(xml_data)
print("JSONをXMLに変換しました。")
|