Qtの基礎 - XML
概要
XMLは、データを構造化して記述するためのマークアップ言語である。
1998年にW3Cによって策定されて以来、様々な分野で広く使用されている。
XMLの大きな特徴は、その拡張性にある。
ユーザが独自のタグを定義できるため、多様なデータ構造を表現することが可能である。
また、人間にも機械にも読みやすい形式で記述されるため、データの可読性が高いというメリットがある。
XMLドキュメントは、通常、プロローグから始まり、その後にルート要素が続く。
ルート要素の中には、階層構造で子要素が配置される。
各要素には属性を付けることができ、要素内にはテキストデータを含めることができる。
この構造により、XMLは様々な用途に適している。
例えば、システム間でのデータ交換、アプリケーションの設定ファイル、Webサービスのデータ形式、データベースの保存形式、文書形式としても利用されている。
XMLに関連する技術も多く存在しており、DTDやXML Schemaはドキュメントの構造を定義するために使用され、XPathはXML文書内の特定の要素や属性を指定するのに役立つ。
また、XSLTを使用すればXML文書を他の形式に変換することができる。
XMLの長所としては、その柔軟性の高さや自己記述的な性質、データと表示の分離が可能であること等が挙げられる。
一方で、XMLは冗長になりがちで、ファイルサイズが大きくなる傾向があること、パース処理に時間がかかる場合があること等が短所として指摘されている。
QXmlStreamReaderとQt XMLモジュールの違い
QXmlStreamReaderとQt XMLモジュールの主なクラス (例: QDomDocumentクラス等) には、いくつかの違いがある。
QXmlStreamReader
クラスは現代のXML処理タスクに適しており、大きなファイルを扱う場合やメモリ効率と処理速度が重要な場合に推奨される。
一方、Qt XMLモジュール (QDomDocumentクラス) は小さなXMLドキュメントの簡単な操作やドキュメント全体の構造を変更する必要がある場合に適している。
特別な理由がない限り、QXmlStreamReader
クラスの使用が推奨されている。
- パーシング方式
- QXmlStreamReaderクラス
- プル型パーサー。
- 開発者が明示的に次の要素を読み取る必要がある。
- Qt XMLモジュール (QDomDocumentクラス)
- DOM (Document Object Model) ベース。
- XMLドキュメント全体をメモリに読み込む。
- QXmlStreamReaderクラス
- メモリ使用
- QXmlStreamReaderクラス
- メモリ効率が良く、大きなXMLファイルの処理に適している。
- Qt XMLモジュール (QDomDocumentクラス)
- 全ドキュメントをメモリに読み込むため、大きなファイルの処理には多くのメモリを必要とする。
- QXmlStreamReaderクラス
- 処理速度
- QXmlStreamReaderクラス
- 高速であり、特に大きなファイルの処理に効率的である。
- Qt XMLモジュール (QDomDocumentクラス)
- 小さなファイルでは高速であるが、大きなファイルの処理は遅くなる可能性がある。
- QXmlStreamReaderクラス
- 使いやすさ
- QXmlStreamReaderクラス
- より低レベルなAPI。
- XMLの構造に沿って手動でパースする必要がある。
- Qt XMLモジュール (QDomDocumentクラス)
- より高レベルなAPI。
- ドキュメント全体を簡単に操作できる。
- QXmlStreamReaderクラス
- 機能
- QXmlStreamReaderクラス
- 読み取り専用。
- XMLの書き込みには、
QXmlStreamWriter
クラスが必要である。
- Qt XMLモジュール (QDomDocumentクラス)
- 読み取りと書き込みの両方が可能である。
- また、ドキュメントの構造を変更することもできる。
- QXmlStreamReaderクラス
- 名前空間サポート
- QXmlStreamReaderクラス
- 名前空間を完全にサポートしている。
- Qt XMLモジュール (QDomDocumentクラス)
- 名前空間のサポートは限定的である。
- QXmlStreamReaderクラス
- バージョン
- QXmlStreamReaderクラス
- Qt 4.3以降で使用可能である。
- Qt XMLモジュール (QDomDocumentクラス)
- 古いバージョンのQtから使用可能であるが、新しいプロジェクトでは非推奨である。
- QXmlStreamReaderクラス
- 標準準拠
- QXmlStreamReaderクラス
- XML 1.0およびXML 1.1規格に完全準拠している。
- Qt XMLモジュール (QDomDocumentクラス)
- 完全な準拠ではない。
- また、一部の高度な機能が欠けている可能性がある。
- QXmlStreamReaderクラス
QXmlStreamReaderクラス
QXmlStreamReaderクラスとは
QXmlStreamReaderクラスは、XMLをシンプルなストリーミングAPIで読み込むための高速パーサである。
ストリームリーダの基本的なコンセプトは、XMLドキュメントをトークンのストリームとして読み込むことである。
QXmlStreamReaderクラスとSAXの主な違いは、これらのXMLトークンの読み込み手順である。
- SAXの場合
- アプリケーションはパーサの都合に合わせてパーサからXMLイベントを受信するハンドラ (コールバック関数) を提供する必要がある。
- QXmlStreamReaderの場合
- 繰り返し文を使用して、必要なトークンを次々にリーダから取り出すことができる。
- これは、
readNext
メソッドを呼び出すことで実行され、リーダは次のトークンを完了するまで入力ストリームから読み取り、tokenType
メソッドを返す。 - その後、
isStartElement
メソッドやtext
メソッド等を使用してトークンを確認することにより、読み込まれているタグや要素についての情報を得ることができる。
このプルアプローチのメリットは、再帰降順パーサを構築して、XMLを異なるメソッドやクラスに分割できることである。
これにより、XMLの解析を簡単に追跡することができる。
要素の取得例
以下の例では、XMLファイルを読み込み、以下に示す要素を読み込んでいる。
- <Hypocenter> -> <Area> -> <Name>の値
- <Hypocenter> -> <Area> -> <Code>のtype属性の値
- 全ての<Observation> -> <IntensityStation> -> <Name>の値
読み込むXMLファイルを以下に示す。
<!-- 使用するXMLファイル -->
<Earthquake>
<OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
<ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
<Hypocenter>
<Area>
<Name>茨城県南部</Name>
<Code type="震央地名">301</Code>
</Area>
</Hypocenter>
<jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
</Earthquake>
<Observation>
<Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
<Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
<City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
<IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
<IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
</City>
<City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
<IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
</City>
</Area>
</Pref>
</Observation>
- Qtプロジェクトファイルを使用する場合
# Qtプロジェクトファイル
QT += xml
- CMakeLists.txtファイルを使用する場合
# CMakeLists.txtファイル
# ...略
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
# ...略
target_link_libraries(<ターゲット名> PRIVATE
Qt${QT_VERSION_MAJOR}::Core
)
#include <QCoreApplication>
#include <QXmlStreamReader>
#include <QFile>
#include <QDebug>
void parseXml(const QString& xmlData)
{
QXmlStreamReader xml(xmlData);
QString hypocenterAreaName;
QString hypocenterAreaCodeType;
QStringList intensityStationNames;
while (!xml.atEnd() && !xml.hasError()) {
QXmlStreamReader::TokenType token = xml.readNext();
if (token == QXmlStreamReader::StartElement) {
if (xml.name() == "Hypocenter") {
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "Hypocenter")) {
if (xml.tokenType() == QXmlStreamReader::StartElement) {
if (xml.name() == "Area") {
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "Area")) {
if (xml.tokenType() == QXmlStreamReader::StartElement) {
if (xml.name() == "Name") {
hypocenterAreaName = xml.readElementText();
}
else if (xml.name() == "Code") {
hypocenterAreaCodeType = xml.attributes().value("type").toString();
}
}
xml.readNext();
}
}
}
xml.readNext();
}
}
else if (xml.name() == "Observation") {
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "Observation")) {
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == "IntensityStation") {
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "IntensityStation")) {
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == "Name") {
intensityStationNames.append(xml.readElementText());
}
xml.readNext();
}
}
xml.readNext();
}
}
}
}
if (xml.hasError()) {
qDebug() << "XMLエラー: " << xml.errorString();
}
else {
qDebug() << "Hypocenter Area Name: " << hypocenterAreaName;
qDebug() << "Hypocenter Area Code Type: " << hypocenterAreaCodeType;
qDebug() << "Intensity Station Names: ";
for (const auto& name : intensityStationNames) {
qDebug() << " -" << name;
}
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QFile file("sample.xml");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "XMLファイルのオープンに失敗";
return -1;
}
QString xmlData = file.readAll();
file.close();
parseXml(xmlData);
return a.exec();
}
QXmlStreamWriterクラス
QXmlStreamWriterクラスとは
QXmlStreamWriter
クラスは、XMLデータをシンプルなストリーミングAPIで生成するためのクラスである。
このクラスは、テキストベースのXMLデータを効率的かつ簡単に書き出すために使用される。
QXmlStreamWriter
クラスの基本的なコンセプトは、XMLドキュメントを順次書き出すことであり、
エレメントの開始や終了、属性の追加、テキストの書き出し等、XMLの様々な部分を逐次的に処理することができる。
主な機能を以下に示す。
- エレメントの開始と終了
writeStartElement
メソッドを使用して、エレメントを開始する。- この時点でエレメント名を指定し、そのエレメントが必要とする属性を追加できる。
writeEndElement
メソッドを呼び出すことにより、そのエレメントの終了タグを自動的に生成する。
- 属性の追加
writeAttribute
メソッドで、エレメントに属性を追加できる。- 属性名と値を指定するだけで簡単に追加が可能である。
- テキストの書き出し
writeCharacters
メソッドを使用して、エレメントの中にテキストデータを書き込むことができる。
- XMLの自動管理
QXmlStreamWriter
クラスは、開始したエレメントを適切に終了するため、XMLの整合性を保つのに役立つ。- また、エンコーディングの指定やXML宣言の追加も簡単に行うことができる。
QXmlStreamWriter
クラスの主なメリットは、そのシンプルなインターフェースと効率性にある。
ストリーミングAPIのため、大量のデータを1度にメモリに保持することなく、逐次的に処理することができる。
これにより、大規模なXMLドキュメントの生成や動的なデータの書き出しが容易になる。
XMLファイルの作成
以下の例では、指定されたXML構造を持つXMLファイルを作成している。
<!-- 作成するXMLファイルの構造 -->
<Earthquake>
<OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
<ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
<Hypocenter>
<Area>
<Name>茨城県南部</Name>
<Code type="震央地名">301</Code>
</Area>
</Hypocenter>
<jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
</Earthquake>
<Observation>
<Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
<Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
<City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
<IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
<IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
</City>
<City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
<IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
</City>
</Area>
</Pref>
</Observation>
#include <QCoreApplication>
#include <QXmlStreamWriter>
#include <QFile>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QFile file("sample.xml");
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "Failed to open file for writing";
return -1;
}
QXmlStreamWriter xmlWriter(&file);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("Earthquake");
xmlWriter.writeTextElement("OriginTime", "2024-08-23T21:00:00+09:00");
xmlWriter.writeTextElement("ArrivalTime", "2024-08-23T21:01:00+09:00");
xmlWriter.writeStartElement("Hypocenter");
xmlWriter.writeStartElement("Area");
xmlWriter.writeTextElement("Name", "茨城県南部");
xmlWriter.writeStartElement("Code");
xmlWriter.writeAttribute("type", "震央地名");
xmlWriter.writeCharacters("301");
xmlWriter.writeEndElement(); // Code
xmlWriter.writeEndElement(); // Area
xmlWriter.writeEndElement(); // Hypocenter
xmlWriter.writeStartElement("jmx_eb:Magnitude");
xmlWriter.writeAttribute("type", "Mj");
xmlWriter.writeAttribute("description", "M3.8");
xmlWriter.writeCharacters("3.8");
xmlWriter.writeEndElement(); // jmx_eb:Magnitude
xmlWriter.writeEndElement(); // Earthquake
xmlWriter.writeStartElement("Observation");
xmlWriter.writeStartElement("Pref");
xmlWriter.writeTextElement("Name", "茨城県");
xmlWriter.writeTextElement("Code", "08");
xmlWriter.writeTextElement("MaxInt", "2");
xmlWriter.writeStartElement("Area");
xmlWriter.writeTextElement("Name", "茨城県北部");
xmlWriter.writeTextElement("Code", "300");
xmlWriter.writeTextElement("MaxInt", "2");
xmlWriter.writeStartElement("City");
xmlWriter.writeTextElement("Name", "小美玉市");
xmlWriter.writeTextElement("Code", "0823600");
xmlWriter.writeTextElement("MaxInt", "2");
xmlWriter.writeStartElement("IntensityStation");
xmlWriter.writeTextElement("Name", "小美玉市小川*");
xmlWriter.writeTextElement("Code", "0823633");
xmlWriter.writeTextElement("Int", "2");
xmlWriter.writeEndElement(); // IntensityStation
xmlWriter.writeStartElement("IntensityStation");
xmlWriter.writeTextElement("Name", "小美玉市上玉里*");
xmlWriter.writeTextElement("Code", "0823635");
xmlWriter.writeTextElement("Int", "2");
xmlWriter.writeEndElement(); // IntensityStation
xmlWriter.writeEndElement(); // City
xmlWriter.writeStartElement("City");
xmlWriter.writeTextElement("Name", "水戸市");
xmlWriter.writeTextElement("Code", "0820100");
xmlWriter.writeTextElement("MaxInt", "1");
xmlWriter.writeStartElement("IntensityStation");
xmlWriter.writeTextElement("Name", "水戸市千波町*");
xmlWriter.writeTextElement("Code", "0820121");
xmlWriter.writeTextElement("Int", "1");
xmlWriter.writeEndElement(); // IntensityStation
xmlWriter.writeEndElement(); // City
xmlWriter.writeEndElement(); // Area
xmlWriter.writeEndElement(); // Pref
xmlWriter.writeEndElement(); // Observation
xmlWriter.writeEndDocument();
file.close();
return a.exec();
}
libxml2ライブラリ
libxml2ライブラリとは
libxml2ライブラリは、XMLの解析と操作を行うための広く使用されているオープンソースライブラリである。
C言語で記述されており、多くのプログラミング言語から利用できる。
効率的なメモリ管理と高速な処理を実現しており、様々なOSで動作する。
libxml2ライブラリは、XMLドキュメントの読み込み、解析、作成、変更、検証といった基本的な機能を提供している。
XMLの構文解析においては、DOMとSAXの両方のアプローチをサポートしている。
DOMは文書全体をメモリ上に木構造として読み込むため、文書全体を操作する必要がある場合に適している。
一方、SAXはイベントベースの解析を行い、大規模な文書を効率的に処理する場合に有用である。
また、libxml2ライブラリは、XMLスキーマやDTDを用いた文書の検証、XPath式を使用したXML文書内の特定の要素や属性の検索、XSLT変換の実行等、
XMLに関連する多くの標準技術もサポートしている。
セキュリティ面では、libxml2ライブラリは入力の検証やエンティティの展開制限などの機能を備えており、XML外部実体攻撃 (XXE) 等の脆弱性に対する保護を提供する。
上記の特徴により、libxml2ライブラリはWebサービス、CMS、データ交換アプリケーション等、XMLを扱う多くのソフトウェアプロジェクトで採用されている。
libxml2ライブラリのライセンス
libxml2ライブラリのライセンスは、MITライセンスに準拠している。
libxml2ライブラリのインストール
パッケージ管理システムからインストール
# RHEL sudo dnf install libxml2-devel # SUSE sudo zypper install libxml2-devel
ソースコードからインストール
libxml2ライブラリのビルドに必要なライブラリをインストールする。
# RHEL sudo dnf install cmake python3-devel \ zlib-devel # zlibライブラリを使用する場合 xz-devel # lzmaライブラリを使用する場合 readline-devel # Readlineライブラリを使用する場合 libicu-devel # ICUライブラリを使用する場合 meson ninja-build # MesonおよびNinjaでビルドする場合 # SUSE sudo zypper install cmake python3-devel \ zlib-devel # zlibライブラリを使用する場合 xz-devel # lzmaライブラリを使用する場合 readline-devel # Readlineライブラリを使用する場合 libicu-devel # ICUライブラリを使用する場合 meson ninja # MesonおよびNinjaでビルドする場合
libxml2ライブラリのGithub、または、libxml2ライブラリのGithubにアクセスして、ソースコードをダウンロードする。
ダウンロードしたファイルを解凍する。
tar xf libxml2-v<バージョン>.tar.gz cd libxml2-v<バージョン>
libxml2ライブラリをビルドおよびインストールする。
mkdir build && cd build # CMakeを使用する場合 cmake -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=<libxml2ライブラリのインストールディレクトリ> \ -DLIBXML2_WITH_HTTP=ON \ # HTTPをサポートする場合 -DLIBXML2_WITH_READLINE=ON \ # Readlineを使用する場合 -DLIBXML2_WITH_ICU=ON \ # ICUライブラリを使用する場合 -DLIBXML2_WITH_ZLIB=ON \ # zlibライブラリを使用する場合 -DLIBXML2_WITH_LZMA=ON \ # lzmaライブラリを使用する場合 .. make -j $(nproc) make install # MesonおよびNinjaを使用する場合 meson setup ./build \ -Dprefix=<libxml2ライブラリのインストールディレクトリ> \ -Dhttp=enabled \ -Dicu=enabled \ -Dlzma=enabled \ -Dzlib=enabled ninja -C ./build ninja -C ./build install
Qtプロジェクトファイル (.pro) を使用する場合
# Qtプロジェクトファイル (.pro)
# Pkg-configを使用する場合
CONFIG += link_pkgconfig
PKGCONFIG += libxml-2.0
# Pkg-Configを使用しない場合
INCLUDEPATH += /usr/include/libxml2
LIBS += -lxml2
CMakeを使用する場合
# CMakeLists.txtファイル
# pkg-configを使うための準備
find_package(PkgConfig REQUIRED)
# pkg-configを使用してlibxml2ライブラリを検索
pkg_search_module(LIBXML2 REQUIRED libxml-2.0)
# ライブラリのインクルードディレクトリをターゲットに追加
include_directories(${LIBXML2_INCLUDE_DIRS})
# ライブラリのリンクディレクトリをターゲットに追加
link_directories(${LIBXML2_LIBRARY_DIRS})
target_include_directories(<プロジェクト名> PRIVATE
# ...略
${LIBXML2_INCLUDE_DIRS}
)
target_link_libraries(<プロジェクト名>
# ...略
${LIBXML2_LIBRARIES}
)
# libxml2のコンパイルオプション
add_definitions(
# ...略
${LIBXML2_CFLAGS_OTHER}
)
要素の取得
以下の例では、指定されたXML構造を持つXMLファイルを読み込み取得している。
読み込むXMLファイルを以下に示す。
<!-- 使用するXMLファイル -->
<Earthquake>
<OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
<ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
<Hypocenter>
<Area>
<Name>茨城県南部</Name>
<Code type="震央地名">301</Code>
</Area>
</Hypocenter>
<jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
</Earthquake>
<Observation>
<Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
<Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
<City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
<IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
<IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
</City>
<City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
<IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
</City>
</Area>
</Pref>
</Observation>
// XMLParser.hファイル
#ifndef XMLPARSER_H
#define XMLPARSER_H
#include <QString>
#include <QVector>
#include <libxml/parser.h>
#include <libxml/tree.h>
class XMLParser {
private:
static QString getNodeContent(xmlNodePtr node);
public:
static QString getHypocenterAreaName(const QString& xmlContent);
static QString getHypocenterAreaCodeType(const QString& xmlContent);
static QVector<QString> getIntensityStationNames(const QString& xmlContent);
};
QString XMLParser::getNodeContent(xmlNodePtr node)
{
if (node && node->children && node->children->content) {
return QString::fromUtf8(reinterpret_cast<const char*>(node->children->content));
}
return QString();
}
QString XMLParser::getHypocenterAreaName(const QString& xmlContent)
{
xmlDocPtr doc = xmlReadMemory(xmlContent.toUtf8().constData(), xmlContent.toUtf8().size(), NULL, NULL, 0);
if (!doc) return QString();
xmlNodePtr root = xmlDocGetRootElement(doc);
xmlNodePtr current = root;
while (current) {
if (QString::fromUtf8(reinterpret_cast<const char*>(current->name)) == "Hypocenter") {
xmlNodePtr area = current->children;
while (area) {
if (QString::fromUtf8(reinterpret_cast<const char*>(area->name)) == "Area") {
xmlNodePtr name = area->children;
while (name) {
if (QString::fromUtf8(reinterpret_cast<const char*>(name->name)) == "Name") {
QString result = getNodeContent(name);
xmlFreeDoc(doc);
return result;
}
name = name->next;
}
}
area = area->next;
}
}
current = current->next;
}
xmlFreeDoc(doc);
return QString();
}
QString XMLParser::getHypocenterAreaCodeType(const QString& xmlContent)
{
xmlDocPtr doc = xmlReadMemory(xmlContent.toUtf8().constData(), xmlContent.toUtf8().size(), NULL, NULL, 0);
if (!doc) return QString();
xmlNodePtr root = xmlDocGetRootElement(doc);
xmlNodePtr current = root;
while (current) {
if (QString::fromUtf8(reinterpret_cast<const char*>(current->name)) == "Hypocenter") {
xmlNodePtr area = current->children;
while (area) {
if (QString::fromUtf8(reinterpret_cast<const char*>(area->name)) == "Area") {
xmlNodePtr code = area->children;
while (code) {
if (QString::fromUtf8(reinterpret_cast<const char*>(code->name)) == "Code") {
xmlChar *type = xmlGetProp(code, reinterpret_cast<const xmlChar*>("type"));
if (type) {
QString result = QString::fromUtf8(reinterpret_cast<const char*>(type));
xmlFree(type);
xmlFreeDoc(doc);
return result;
}
}
code = code->next;
}
}
area = area->next;
}
}
current = current->next;
}
xmlFreeDoc(doc);
return QString();
}
QVector<QString> XMLParser::getIntensityStationNames(const QString& xmlContent)
{
QVector<QString> results;
xmlDocPtr doc = xmlReadMemory(xmlContent.toUtf8().constData(), xmlContent.toUtf8().size(), NULL, NULL, 0);
if (!doc) return results;
xmlNodePtr root = xmlDocGetRootElement(doc);
xmlNodePtr current = root;
while (current) {
if (QString::fromUtf8(reinterpret_cast<const char*>(current->name)) == "Observation") {
xmlNodePtr pref = current->children;
while (pref) {
if (QString::fromUtf8(reinterpret_cast<const char*>(pref->name)) == "Pref") {
xmlNodePtr area = pref->children;
while (area) {
if (QString::fromUtf8(reinterpret_cast<const char*>(area->name)) == "Area") {
xmlNodePtr city = area->children;
while (city) {
if (QString::fromUtf8(reinterpret_cast<const char*>(city->name)) == "City") {
xmlNodePtr station = city->children;
while (station) {
if (QString::fromUtf8(reinterpret_cast<const char*>(station->name)) == "IntensityStation") {
xmlNodePtr name = station->children;
while (name) {
if (QString::fromUtf8(reinterpret_cast<const char*>(name->name)) == "Name") {
results.append(getNodeContent(name));
}
name = name->next;
}
}
station = station->next;
}
}
city = city->next;
}
}
area = area->next;
}
}
pref = pref->next;
}
}
current = current->next;
}
xmlFreeDoc(doc);
return results;
}
#endif // XMLPARSER_H
#include <QCoreApplication>
#include <QFile>
#include "xmlparser.h"
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QFile file("earthquake.xml");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Failed to open file";
return -1;
}
QString xmlContent = file.readAll();
file.close();
QString hypocenterAreaName = XMLParser::getHypocenterAreaName(xmlContent);
QString hypocenterAreaCodeType = XMLParser::getHypocenterAreaCodeType(xmlContent);
QVector<QString> intensityStationNames = XMLParser::getIntensityStationNames(xmlContent);
qDebug() << "Hypocenter Area Name:" << hypocenterAreaName;
qDebug() << "Hypocenter Area Code Type:" << hypocenterAreaCodeType;
qDebug() << "Intensity Station Names:";
for (const auto& name : intensityStationNames) {
qDebug() << " -" << name;
}
return a.exec();
}
XMLファイルの作成
以下の例では、指定されたXML構造を持つXMLファイルを作成している。
<!-- 作成するXMLファイルの構造 -->
<Earthquake>
<OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
<ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
<Hypocenter>
<Area>
<Name>茨城県南部</Name>
<Code type="震央地名">301</Code>
</Area>
</Hypocenter>
<jmx_eb:Magnitude type="Mj" description="M3.8">3.8</jmx_eb:Magnitude>
</Earthquake>
<Observation>
<Pref><Name>茨城県</Name><Code>08</Code><MaxInt>2</MaxInt>
<Area><Name>茨城県北部</Name><Code>300</Code><MaxInt>2</MaxInt>
<City><Name>小美玉市</Name><Code>0823600</Code><MaxInt>2</MaxInt>
<IntensityStation><Name>小美玉市小川*</Name><Code>0823633</Code><Int>2</Int></IntensityStation>
<IntensityStation><Name>小美玉市上玉里*</Name><Code>0823635</Code><Int>2</Int></IntensityStation>
</City>
<City><Name>水戸市</Name><Code>0820100</Code><MaxInt>1</MaxInt>
<IntensityStation><Name>水戸市千波町*</Name><Code>0820121</Code><Int>1</Int></IntensityStation>
</City>
</Area>
</Pref>
</Observation>
// XMLCreator.hファイル
#ifndef XMLCREATOR_H
#define XMLCREATOR_H
#include <QString>
#include <QVector>
#include <libxml/tree.h>
class XMLCreator {
public:
static void createXML(const QString& filename,
const QString& hypocenterAreaName,
const QString& hypocenterAreaCodeType,
const QVector<QString>& intensityStationNames);
private:
static xmlNodePtr createHypocenter(xmlDocPtr doc, const QString& areaName, const QString& codeType);
static xmlNodePtr createObservation(xmlDocPtr doc, const QVector<QString>& stationNames);
};
void XMLCreator::createXML(const QString& filename,
const QString& hypocenterAreaName,
const QString& hypocenterAreaCodeType,
const QVector<QString>& intensityStationNames)
{
xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
xmlNodePtr root = xmlNewNode(NULL, BAD_CAST "Report");
xmlDocSetRootElement(doc, root);
xmlNodePtr earthquake = xmlNewChild(root, NULL, BAD_CAST "Earthquake", NULL);
xmlNewChild(earthquake, NULL, BAD_CAST "OriginTime", BAD_CAST "2024-08-23T21:00:00+09:00");
xmlNewChild(earthquake, NULL, BAD_CAST "ArrivalTime", BAD_CAST "2024-08-23T21:01:00+09:00");
xmlAddChild(earthquake, createHypocenter(doc, hypocenterAreaName, hypocenterAreaCodeType));
xmlNodePtr magnitude = xmlNewChild(earthquake, NULL, BAD_CAST "jmx_eb:Magnitude", BAD_CAST "3.8");
xmlNewProp(magnitude, BAD_CAST "type", BAD_CAST "Mj");
xmlNewProp(magnitude, BAD_CAST "description", BAD_CAST "M3.8");
xmlAddChild(root, createObservation(doc, intensityStationNames));
xmlSaveFormatFileEnc(filename.toUtf8().constData(), doc, "UTF-8", 1);
xmlFreeDoc(doc);
xmlCleanupParser();
}
xmlNodePtr XMLCreator::createHypocenter(xmlDocPtr doc, const QString& areaName, const QString& codeType)
{
xmlNodePtr hypocenter = xmlNewNode(NULL, BAD_CAST "Hypocenter");
xmlNodePtr area = xmlNewChild(hypocenter, NULL, BAD_CAST "Area", NULL);
xmlNewChild(area, NULL, BAD_CAST "Name", BAD_CAST areaName.toUtf8().constData());
xmlNodePtr code = xmlNewChild(area, NULL, BAD_CAST "Code", BAD_CAST "301");
xmlNewProp(code, BAD_CAST "type", BAD_CAST codeType.toUtf8().constData());
return hypocenter;
}
xmlNodePtr XMLCreator::createObservation(xmlDocPtr doc, const QVector<QString>& stationNames)
{
xmlNodePtr observation = xmlNewNode(NULL, BAD_CAST "Observation");
xmlNodePtr pref = xmlNewChild(observation, NULL, BAD_CAST "Pref", NULL);
xmlNewChild(pref, NULL, BAD_CAST "Name", BAD_CAST "茨城県");
xmlNewChild(pref, NULL, BAD_CAST "Code", BAD_CAST "08");
xmlNewChild(pref, NULL, BAD_CAST "MaxInt", BAD_CAST "2");
xmlNodePtr area = xmlNewChild(pref, NULL, BAD_CAST "Area", NULL);
xmlNewChild(area, NULL, BAD_CAST "Name", BAD_CAST "茨城県北部");
xmlNewChild(area, NULL, BAD_CAST "Code", BAD_CAST "300");
xmlNewChild(area, NULL, BAD_CAST "MaxInt", BAD_CAST "2");
xmlNodePtr city = xmlNewChild(area, NULL, BAD_CAST "City", NULL);
xmlNewChild(city, NULL, BAD_CAST "Name", BAD_CAST "小美玉市");
xmlNewChild(city, NULL, BAD_CAST "Code", BAD_CAST "0823600");
xmlNewChild(city, NULL, BAD_CAST "MaxInt", BAD_CAST "2");
for (const auto& name : stationNames) {
xmlNodePtr station = xmlNewChild(city, NULL, BAD_CAST "IntensityStation", NULL);
xmlNewChild(station, NULL, BAD_CAST "Name", BAD_CAST name.toUtf8().constData());
xmlNewChild(station, NULL, BAD_CAST "Code", BAD_CAST "0823633");
xmlNewChild(station, NULL, BAD_CAST "Int", BAD_CAST "2");
}
return observation;
}
#endif // XMLCREATOR_H
#include <QCoreApplication>
#include <QFile>
#include "XMLCreator.h"
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString hypocenterAreaName = "茨城県南部";
QString hypocenterAreaCodeType = "震央地名";
QVector<QString> intensityStationNames = {"小美玉市小川*", "小美玉市上玉里*", "水戸市千波町*"};
QString filename = "earthquake_output.xml";
XMLCreator::createXML(filename, hypocenterAreaName, hypocenterAreaCodeType, intensityStationNames);
// ファイルの内容を読み込んで表示
QFile file(filename);
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
QString content = in.readAll();
qDebug() << "Created XML content:";
qDebug().noquote() << content;
file.close();
}
else {
qDebug() << "Failed to open the created XML file.";
}
return a.exec();
}