「Qtの基礎 - XML」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

文字列「__FORCETOC__」を「{{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This pag…
 
(同じ利用者による、間の16版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
XMLは、データを構造化して記述するためのマークアップ言語である。<br>
1998年にW3Cによって策定されて以来、様々な分野で広く使用されている。<br>
<br>
XMLの大きな特徴は、その拡張性にある。<br>
ユーザが独自のタグを定義できるため、多様なデータ構造を表現することが可能である。<br>
また、人間にも機械にも読みやすい形式で記述されるため、データの可読性が高いというメリットがある。<br>
<br>
XMLドキュメントは、通常、プロローグから始まり、その後にルート要素が続く。<br>
ルート要素の中には、階層構造で子要素が配置される。<br>
各要素には属性を付けることができ、要素内にはテキストデータを含めることができる。<br>
<br>
この構造により、XMLは様々な用途に適している。<br>
例えば、システム間でのデータ交換、アプリケーションの設定ファイル、Webサービスのデータ形式、データベースの保存形式、文書形式としても利用されている。<br>
<br>
XMLに関連する技術も多く存在しており、DTDやXML Schemaはドキュメントの構造を定義するために使用され、XPathはXML文書内の特定の要素や属性を指定するのに役立つ。<br>
また、XSLTを使用すればXML文書を他の形式に変換することができる。<br>
<br>
XMLの長所としては、その柔軟性の高さや自己記述的な性質、データと表示の分離が可能であること等が挙げられる。<br>
一方で、XMLは冗長になりがちで、ファイルサイズが大きくなる傾向があること、パース処理に時間がかかる場合があること等が短所として指摘されている。<br>
<br><br>
== QXmlStreamReaderとQt XMLモジュールの違い ==
QXmlStreamReaderとQt XMLモジュールの主なクラス (例: QDomDocumentクラス等) には、いくつかの違いがある。<br>
<br>
<code>QXmlStreamReader</code>クラスは現代のXML処理タスクに適しており、大きなファイルを扱う場合やメモリ効率と処理速度が重要な場合に推奨される。<br>
一方、Qt XMLモジュール (QDomDocumentクラス) は小さなXMLドキュメントの簡単な操作やドキュメント全体の構造を変更する必要がある場合に適している。<br>
<br>
<u>特別な理由がない限り、<code>QXmlStreamReader</code>クラスの使用が推奨されている。</u><br>
<br>
* パーシング方式
** QXmlStreamReaderクラス
**: プル型パーサー。
**: 開発者が明示的に次の要素を読み取る必要がある。
** Qt XMLモジュール (QDomDocumentクラス)
**: DOM (Document Object Model) ベース。
**: XMLドキュメント全体をメモリに読み込む。
*: <br>
* メモリ使用
** QXmlStreamReaderクラス
**: メモリ効率が良く、大きなXMLファイルの処理に適している。
** Qt XMLモジュール (QDomDocumentクラス)
**: 全ドキュメントをメモリに読み込むため、大きなファイルの処理には多くのメモリを必要とする。
*: <br>
* 処理速度
** QXmlStreamReaderクラス
**: 高速であり、特に大きなファイルの処理に効率的である。
** Qt XMLモジュール (QDomDocumentクラス)
**: 小さなファイルでは高速であるが、大きなファイルの処理は遅くなる可能性がある。
*: <br>
* 使いやすさ
** QXmlStreamReaderクラス
**: より低レベルなAPI。
**: XMLの構造に沿って手動でパースする必要がある。
** Qt XMLモジュール (QDomDocumentクラス)
**: より高レベルなAPI。
**: ドキュメント全体を簡単に操作できる。
*: <br>
* 機能
** QXmlStreamReaderクラス
**: 読み取り専用。
**: XMLの書き込みには、<code>QXmlStreamWriter</code>クラスが必要である。
** Qt XMLモジュール (QDomDocumentクラス)
**: 読み取りと書き込みの両方が可能である。
**: また、ドキュメントの構造を変更することもできる。
*: <br>
* 名前空間サポート
** QXmlStreamReaderクラス
**: 名前空間を完全にサポートしている。
** Qt XMLモジュール (QDomDocumentクラス)
**: 名前空間のサポートは限定的である。
*: <br>
* バージョン
** QXmlStreamReaderクラス
**: Qt 4.3以降で使用可能である。
** Qt XMLモジュール (QDomDocumentクラス)
**: 古いバージョンのQtから使用可能であるが、新しいプロジェクトでは非推奨である。
*: <br>
* 標準準拠
** QXmlStreamReaderクラス
**: XML 1.0およびXML 1.1規格に完全準拠している。
** Qt XMLモジュール (QDomDocumentクラス)
**: 完全な準拠ではない。
**: また、一部の高度な機能が欠けている可能性がある。
<br><br>
<br><br>


20行目: 103行目:
<br>
<br>
==== 要素の取得例 ====
==== 要素の取得例 ====
以下の例では、XMLファイルを読み込み、要素titleの値を抽出してコンソールへ出力している。<br>
以下の例では、XMLファイルを読み込み、以下に示す要素を読み込んでいる。<br>
* <Hypocenter>  ->  <Area>  ->  <Name>の値
* <Hypocenter>  ->  <Area>  ->  <nowiki><Code></nowiki>のtype属性の値
* 全ての<Observation>  ->  <IntensityStation>  ->  <Name>の値
<br>
読み込むXMLファイルを以下に示す。<br>
<syntaxhighlight lang="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>
</syntaxhighlight>
<br>
* Qtプロジェクトファイルを使用する場合
<syntaxhighlight lang="make">
# Qtプロジェクトファイル
QT += xml
</syntaxhighlight>
<br>
* CMakeLists.txtファイルを使用する場合
<syntaxhighlight lang="cmake">
# 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
)
</syntaxhighlight>
<br>
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
#include <QCoreApplication>
  #include <QXmlStreamReader>
  #include <QXmlStreamReader>
  #include <QFile>
  #include <QFile>
  #include <QTextStream>
  #include <QDebug>
   
   
  // XMLファイルを開く
  void parseXml(const QString& xmlData)
QFile File("<XMLファイルのパス  例: hoge.xml>");
  {
  if(!File.open(QIODevice::ReadOnly)) {
    QXmlStreamReader xml(xmlData);
     std::cout << QString("ファイルのオープンに失敗 : %1").arg(File.errorString()) << std::endl;
     QString hypocenterAreaName;
     return;
     QString hypocenterAreaCodeType;
}
    QStringList intensityStationNames;
   
   
// XMLファイルを読み込む
    while (!xml.atEnd() && !xml.hasError()) {
QTextStream InStream(&File);
      QXmlStreamReader::TokenType token = xml.readNext();
QString XMLData = InStream.readAll();
File.close();
   
   
// XMLファイルから特定の要素を抽出
      if (token == QXmlStreamReader::StartElement) {
QXmlStreamReader xml(XMLData);
          if (xml.name() == "Hypocenter") {
while (!xml.atEnd() && !xml.hasError()) {
            while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "Hypocenter")) {
    QXmlStreamReader::TokenType token = xml.readNext();
                if (xml.tokenType() == QXmlStreamReader::StartElement) {
    if (token == QXmlStreamReader::StartElement) {
                  if (xml.name() == "Area") {
      if (xml.name() == "title") {
                      while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "Area")) {
          // 特定の要素が存在する場合は値をコンソールへ出力
                        if (xml.tokenType() == QXmlStreamReader::StartElement) {
          xml.readNext();
                            if (xml.name() == "Name") {
           std::cout << QString("Title : %1").arg(xml.text().toString()).toStdString() << std::endl;
                              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;
        }
     }
     }
  }
  }
   
   
  if (xml.hasError()) {
  int main(int argc, char *argv[])
    std::cerr << QString("XML Error : %1").arg(xml.errorString()).toStdString() << std::endl;
{
    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();
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>


== Xerces-C++ライブラリ ==
== QXmlStreamWriterクラス ==
==== Xerces-C++ライブラリとは ====
==== QXmlStreamWriterクラスとは ====
Apache XMLプロジェクトにより開発されたXMLパーサライブラリである。<br>
<code>QXmlStreamWriter</code>クラスは、XMLデータをシンプルなストリーミングAPIで生成するためのクラスである。<br>
C++で実装されており、XMLドキュメントの解析、生成、変更が可能であり、W3C標準に準拠しており、高度な機能を持つ。<br>
このクラスは、テキストベースのXMLデータを効率的かつ簡単に書き出すために使用される。<br>
<br>
<code>QXmlStreamWriter</code>クラスの基本的なコンセプトは、XMLドキュメントを順次書き出すことであり、<br>
エレメントの開始や終了、属性の追加、テキストの書き出し等、XMLの様々な部分を逐次的に処理することができる。<br>
<br>
主な機能を以下に示す。<br>
* エレメントの開始と終了
*: <code>writeStartElement</code>メソッドを使用して、エレメントを開始する。
*: この時点でエレメント名を指定し、そのエレメントが必要とする属性を追加できる。
*: <code>writeEndElement</code>メソッドを呼び出すことにより、そのエレメントの終了タグを自動的に生成する。
<br>
<br>
Xerces-C++ライブラリは、以下に示す特徴を持つ。<br>
* 属性の追加
* W3C標準準拠
*: <code>writeAttribute</code>メソッドで、エレメントに属性を追加できる。
*: XML 1.0 / 1.1、DOM Level 1 / 2 / 3、SAX 1 / 2、XInclude、XML Schema、XPath、XSLT、名前空間等のXMLに関連する多くのW3C標準に準拠している。
*: 属性名と値を指定するだけで簡単に追加が可能である。
* 高機能
*: 豊富な機能セットを持っており、XMLドキュメントの解析、検証、変更、生成等が可能である。
*: また、XPathやXSLT等の高度な機能もサポートしている。
* 安定性と信頼性
*: Apache XMLプロジェクトにより長年開発され続けており、安定性と信頼性が高い。
*: エンタープライズアプリケーションでの使用実績がある。
* 拡張性
*: プラグイン機構により、独自の機能を簡単に拡張できる。
*: 例えば、独自のXMLスキーマや文字エンコーディングを追加することが可能である。
* マルチスレッド対応
*: マルチスレッドプログラミングをサポートしている。
<br>
<br>
ただし、Xerces-C++ライブラリは大規模なライブラリであるため、導入や習得にコストがかかる可能性がある。<br>
* テキストの書き出し
また、パフォーマンス面では軽量なライブラリよりも劣ることがある。<br>
*: <code>writeCharacters</code>メソッドを使用して、エレメントの中にテキストデータを書き込むことができる。
<br>
<br>
そのため、プロジェクトの要件次第では、TinyXMLやRapidXml等の軽量ライブラリも選択肢として検討する必要がある。<br>
* XMLの自動管理
*: <code>QXmlStreamWriter</code>クラスは、開始したエレメントを適切に終了するため、XMLの整合性を保つのに役立つ。
*: また、エンコーディングの指定やXML宣言の追加も簡単に行うことができる。
<br>
<br>
一般的には、Xerces-C++ライブラリは高機能で標準準拠のXMLパーサーライブラリであり、本格的なXMLハンドリングが必要な場合に使用する。<br>
<code>QXmlStreamWriter</code>クラスの主なメリットは、そのシンプルなインターフェースと効率性にある。<br>
ストリーミングAPIのため、大量のデータを1度にメモリに保持することなく、逐次的に処理することができる。<br>
これにより、大規模なXMLドキュメントの生成や動的なデータの書き出しが容易になる。<br>
<br>
<br>
==== インストール ====
 
==== XMLファイルの作成 ====
以下の例では、指定されたXML構造を持つXMLファイルを作成している。<br>
<syntaxhighlight lang="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>
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
#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();
}
</syntaxhighlight>
<br><br>
 
== libxml2ライブラリ ==
==== libxml2ライブラリとは ====
libxml2ライブラリは、XMLの解析と操作を行うための広く使用されているオープンソースライブラリである。<br>
C言語で記述されており、多くのプログラミング言語から利用できる。<br>
効率的なメモリ管理と高速な処理を実現しており、様々なOSで動作する。<br>
<br>
libxml2ライブラリは、XMLドキュメントの読み込み、解析、作成、変更、検証といった基本的な機能を提供している。<br>
<br>
XMLの構文解析においては、DOMとSAXの両方のアプローチをサポートしている。<br>
DOMは文書全体をメモリ上に木構造として読み込むため、文書全体を操作する必要がある場合に適している。<br>
一方、SAXはイベントベースの解析を行い、大規模な文書を効率的に処理する場合に有用である。<br>
<br>
また、libxml2ライブラリは、XMLスキーマやDTDを用いた文書の検証、XPath式を使用したXML文書内の特定の要素や属性の検索、XSLT変換の実行等、<br>
XMLに関連する多くの標準技術もサポートしている。
<br>
セキュリティ面では、libxml2ライブラリは入力の検証やエンティティの展開制限などの機能を備えており、XML外部実体攻撃 (XXE) 等の脆弱性に対する保護を提供する。<br>
<br>
上記の特徴により、libxml2ライブラリはWebサービス、CMS、データ交換アプリケーション等、XMLを扱う多くのソフトウェアプロジェクトで採用されている。<br>
<br>
==== libxml2ライブラリのライセンス ====
libxml2ライブラリのライセンスは、MITライセンスに準拠している。<br>
<br>
==== libxml2ライブラリのインストール ====
===== パッケージ管理システムからインストール =====
===== パッケージ管理システムからインストール =====
  # RHEL
  # RHEL
  sudo dnf install xerces-c-devel
  sudo dnf install libxml2-devel
   
   
  # SUSE
  # SUSE
  sudo zypper install libxerces-c-devel
  sudo zypper install libxml2-devel
<br>
<br>
===== ソースコードからインストール =====
===== ソースコードからインストール =====
Xerces-C++ライブラリのビルドに必要なライブラリをインストールする。<br>
libxml2ライブラリのビルドに必要なライブラリをインストールする。<br>
  # RHEL
  # RHEL
  sudo dnf install make cmake gcc gcc-c++ libcurl-devel libicu-devel
  sudo dnf install cmake python3-devel \
                  zlib-devel        # zlibライブラリを使用する場合
                  xz-devel          # lzmaライブラリを使用する場合
                  readline-devel     # Readlineライブラリを使用する場合
                  libicu-devel       # ICUライブラリを使用する場合
                  meson ninja-build  # MesonおよびNinjaでビルドする場合
   
   
  # SUSE
  # SUSE
  sudo zypper install make cmake gcc gcc-c++ libcurl-devel libicu-devel
  sudo zypper install cmake python3-devel \
                    zlib-devel      # zlibライブラリを使用する場合
                    xz-devel        # lzmaライブラリを使用する場合
                    readline-devel # Readlineライブラリを使用する場合
                    libicu-devel   # ICUライブラリを使用する場合
                    meson ninja    # MesonおよびNinjaでビルドする場合
<br>
<br>
[https://xerces.apache.org/xerces-c/download.cgi Xerces-C++ライブラリの公式Webサイト]にアクセスして、ソースコードをダウンロードする。<br>
[https://gitlab.gnome.org/GNOME/libxml2 libxml2ライブラリのGithub]、または、[https://github.com/GNOME/libxml2 libxml2ライブラリのGithub]にアクセスして、ソースコードをダウンロードする。<br>
ダウンロードしたファイルを解凍する。<br>
ダウンロードしたファイルを解凍する。<br>
  tar xf xerces-c-<バージョン>.tar.xz
  tar xf libxml2-v<バージョン>.tar.gz
  cd xerces-c-<バージョン>
  cd libxml2-v<バージョン>
<br>
<br>
Xerces-C++ライブラリをビルドおよびインストールする。<br>
libxml2ライブラリをビルドおよびインストールする。<br>
  mkdir build && cd build
  mkdir build && cd build
   
   
  cmake .. \
# CMakeを使用する場合
       -DCMAKE_INSTALL_PREFIX=<Xerces-C++ライブラリのインストールディレクトリ> \
  cmake -DCMAKE_BUILD_TYPE=Release  \
       -DCMAKE_BUILD_TYPE=Release \
       -DCMAKE_INSTALL_PREFIX=<libxml2ライブラリのインストールディレクトリ> \
       -Dmessage-loader=icu       \  # オプション : 内部UTF-16と他のエンコーディングとの変換に使用するトランスコーダを指定する
       -DLIBXML2_WITH_HTTP=ON      \ # HTTPをサポートする場合
                                    # 他に、inmemoryまたはiconv等も指定することができる
       -DLIBXML2_WITH_READLINE=ON  \  # Readlineを使用する場合
       -Dmessage-loader=icu          # オプション : 診断メッセージへのアクセスに使用するメッセージローダを指定する
      -DLIBXML2_WITH_ICU=ON       \  # ICUライブラリを使用する場合
                                    # 他に、gnuiconvまたはiconvを指定することもできる
       -DLIBXML2_WITH_ZLIB=ON      \  # zlibライブラリを使用する場合
       -Dnetwork-accessor=curl      # オプション : ネットワークリソースへのアクセスに使用するライブラリを指定する
       -DLIBXML2_WITH_LZMA=ON      \  # lzmaライブラリを使用する場合
                                    # 他に、"socket"を指定することもできる
      ..
  make -j $(nproc)
  make -j $(nproc)
  make install
  make install
# MesonおよびNinjaを使用する場合
meson setup ./build                                  \
    -Dprefix=<libxml2ライブラリのインストールディレクトリ> \
    -Dhttp=enabled                                  \
    -Dicu=enabled                                    \
    -Dlzma=enabled                                  \
    -Dzlib=enabled
ninja -C ./build
ninja -C ./build install
<br>
<br>
==== サンプルコード ====
==== Qtプロジェクトファイル (.pro) を使用する場合 ====
サンプルコードでは、以下に示すような手順を行っている。<br>
<syntaxhighlight lang="make">
# まず、<code>XMLPlatformUtils::Initialize</code>メソッドを実行して、Xerces-C++ライブラリを初期化する。
# Qtプロジェクトファイル (.pro)
# 次に、<code>XercesDOMParser</code>クラスのインスタンスを生成して、XMLドキュメントを解析する。
# <code>getDocument</code>メソッドを実行してDOMドキュメントを取得して、<code>getDocumentElement</code>メソッドを実行してルート要素を取得する。
# Pkg-configを使用する場合
# ルート要素の名前を出力する。
CONFIG += link_pkgconfig
# <code>getChildNodes</code>メソッドを実行してルート要素の子要素のリストを取得する。(各子要素の名前を出力)
PKGCONFIG += libxml-2.0
# <code>XMLPlatformUtils::Terminate</code>メソッドを実行して、Xerces-C++ライブラリを解放する。
# Pkg-Configを使用しない場合
INCLUDEPATH += /usr/include/libxml2
LIBS += -lxml2
</syntaxhighlight>
<br>
<br>
以下の例で使用しているXMLファイルの内容を示す。
==== CMakeを使用する場合 ====
<syntaxhighlight lang="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}
)
</syntaxhighlight>
<br>
==== 要素の取得 ====
以下の例では、指定されたXML構造を持つXMLファイルを読み込み取得している。<br>
<br>
読み込むXMLファイルを以下に示す。<br>
  <syntaxhighlight lang="xml">
  <syntaxhighlight lang="xml">
  <?xml version="1.0" encoding="UTF-8"?>
  <!-- 使用するXMLファイル -->
  <root>
    <child1>
<Earthquake>
       <grandchild>Value1</grandchild>
  <OriginTime>2024-08-23T21:00:00+09:00</OriginTime>
    </child1>
  <ArrivalTime>2024-08-23T21:01:00+09:00</ArrivalTime>
    <child2 attr="value">
  <Hypocenter>
       <grandchild>Value2</grandchild>
    <Area>
    </child2>
      <Name>茨城県南部</Name>
    <child3/>
      <Code type="震央地名">301</Code>
  </root>
    </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>
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
  #include <iostream>
// XMLParser.hファイル
  #include <string>
  #include <xercesc/util/PlatformUtils.hpp>
#ifndef XMLPARSER_H
  #include <xercesc/dom/DOM.hpp>
#define XMLPARSER_H
  #include <xercesc/parsers/XercesDOMParser.hpp>
  #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();
   
   
using namespace xercesc;
    xmlNodePtr root = xmlDocGetRootElement(doc);
    xmlNodePtr current = root;
   
   
int main(int argc, char* argv[])
    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)
  {
  {
     try {
     xmlDocPtr doc = xmlReadMemory(xmlContent.toUtf8().constData(), xmlContent.toUtf8().size(), NULL, NULL, 0);
       // Xerces-C++ライブラリの初期化
    if (!doc) return QString();
      XMLPlatformUtils::Initialize();
    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;
    }
   
   
      // XMLドキュメントを解析するパーサーを作成
    xmlFreeDoc(doc);
      XercesDOMParser parser;
      parser.setValidationScheme(XercesDOMParser::Val_Always); // オプションでXMLスキーマ検証を行う
   
   
      // XMLファイルを指定してパースする
    return QString();
      parser.parse("example.xml");
}
   
   
      // ルート要素を取得
QVector<QString> XMLParser::getIntensityStationNames(const QString& xmlContent)
      DOMDocument* document = parser.getDocument();
{
      DOMElement* rootElement = document->getDocumentElement();
    QVector<QString> results;
    xmlDocPtr doc = xmlReadMemory(xmlContent.toUtf8().constData(), xmlContent.toUtf8().size(), NULL, NULL, 0);
    if (!doc) return results;
   
   
      // ルート要素の名前を出力
    xmlNodePtr root = xmlDocGetRootElement(doc);
      char* rootName = XMLString::transcode(rootElement->getTagName());
    xmlNodePtr current = root;
      std::cout << "Root element name: " << rootName << std::endl;
      XMLString::release(&rootName);
   
   
      // 子要素を取得して出力
    while (current) {
       DOMNodeList* childNodes = rootElement->getChildNodes();
       if (QString::fromUtf8(reinterpret_cast<const char*>(current->name)) == "Observation") {
      for (XMLSize_t i = 0; i < childNodes->getLength(); i++) {
          xmlNodePtr pref = current->children;
          DOMNode* childNode = childNodes->item(i);
          while (pref) {
          if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {
            if (QString::fromUtf8(reinterpret_cast<const char*>(pref->name)) == "Pref") {
            DOMElement* childElement = static_cast<DOMElement*>(childNode);
                xmlNodePtr area = pref->children;
            char* childName = XMLString::transcode(childElement->getTagName());
                while (area) {
            std::cout << "Child element name: " << childName << std::endl;
                  if (QString::fromUtf8(reinterpret_cast<const char*>(area->name)) == "Area") {
             XMLString::release(&childName);
                      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
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
#include <QCoreApplication>
#include <QFile>
#include "xmlparser.h"
#include <QDebug>
   
   
      // Xerces-C++ライブラリの終了処理
int main(int argc, char *argv[])
      XMLPlatformUtils::Terminate();
{
    QCoreApplication a(argc, argv);
    QFile file("earthquake.xml");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
      qDebug() << "Failed to open file";
      return -1;
     }
     }
     catch (const XMLException& e) {
       char* message = XMLString::transcode(e.getMessage());
    QString xmlContent = file.readAll();
       std::cout << "Error: " << message << std::endl;
    file.close();
      XMLString::release(&message);
     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();
}
</syntaxhighlight>
<br>
 
==== XMLファイルの作成 ====
以下の例では、指定されたXML構造を持つXMLファイルを作成している。<br>
<br>
<syntaxhighlight lang="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>
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// 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");
     }
     }
     catch (const DOMException& e) {
      char* message = XMLString::transcode(e.msg);
    return observation;
       std::cout << "Error: " << message << std::endl;
}
       XMLString::release(&message);
#endif // XMLCREATOR_H
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
#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();
     }
     }
     catch (...) {
     else {
      std::cout << "Unexpected error occurred" << std::endl;
      qDebug() << "Failed to open the created XML file.";
     }
     }
   
   
     return 0;
     return a.exec();
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br><br>


{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
__FORCETOC__
[[カテゴリ:Qt]]
[[カテゴリ:Qt]]

2024年10月14日 (月) 10:58時点における最新版

概要

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クラス
      メモリ効率が良く、大きなXMLファイルの処理に適している。
    • Qt XMLモジュール (QDomDocumentクラス)
      全ドキュメントをメモリに読み込むため、大きなファイルの処理には多くのメモリを必要とする。

  • 処理速度
    • QXmlStreamReaderクラス
      高速であり、特に大きなファイルの処理に効率的である。
    • Qt XMLモジュール (QDomDocumentクラス)
      小さなファイルでは高速であるが、大きなファイルの処理は遅くなる可能性がある。

  • 使いやすさ
    • QXmlStreamReaderクラス
      より低レベルなAPI。
      XMLの構造に沿って手動でパースする必要がある。
    • Qt XMLモジュール (QDomDocumentクラス)
      より高レベルなAPI。
      ドキュメント全体を簡単に操作できる。

  • 機能
    • QXmlStreamReaderクラス
      読み取り専用。
      XMLの書き込みには、QXmlStreamWriterクラスが必要である。
    • Qt XMLモジュール (QDomDocumentクラス)
      読み取りと書き込みの両方が可能である。
      また、ドキュメントの構造を変更することもできる。

  • 名前空間サポート
    • QXmlStreamReaderクラス
      名前空間を完全にサポートしている。
    • Qt XMLモジュール (QDomDocumentクラス)
      名前空間のサポートは限定的である。

  • バージョン
    • QXmlStreamReaderクラス
      Qt 4.3以降で使用可能である。
    • Qt XMLモジュール (QDomDocumentクラス)
      古いバージョンのQtから使用可能であるが、新しいプロジェクトでは非推奨である。

  • 標準準拠
    • QXmlStreamReaderクラス
      XML 1.0およびXML 1.1規格に完全準拠している。
    • Qt XMLモジュール (QDomDocumentクラス)
      完全な準拠ではない。
      また、一部の高度な機能が欠けている可能性がある。



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();
 }