0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++とboostでXMLを操作してみる

Posted at

やったこと

C++でXMLを操作する機会があったのでboostのBoost Property Tree Libraryを試してみました。

環境

  • VS2022 C++17
  • boost 1.86.0

まずはReadしてみる

XMLはこんな感です。

sample0.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <sample0>
    <child0 color="yellow" id="0">banana</child0>
    <child1 color="red" id="1">apple</child1>
    <child2 color="" id="2">
      <item>melon</item>
      <item>water melon</item>
    </child2>
  </sample0>
</root>

コードは以下の通り。

  • 存在しない要素や属性をgetしてしまうと例外が発生する
  • get_optional()のように~optionalが付く関数を使うと、getできなかった場合にboost::optionalの無効値が返される
  • 属性を取得したい場合は<xmlattr>.属性名で取得できる(削除もできる)
コード
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

using namespace boost::property_tree;
using namespace boost::property_tree::xml_parser;

int main()
{
	// XMLファイルを読み込む
	ptree pt;
	read_xml("sample0.xml", pt, trim_whitespace);

    // <sample0>の子要素を取得     
	if (auto opt = pt.get_child_optional("root.sample0"))
	{	
		for (auto& child : opt.value())
		{
            // 要素名 取得
			std::cout << child.first.c_str() << std::endl;	

			// 属性color 取得
			if (auto opt = child.second.get_optional<std::string>("<xmlattr>.color"))			
			{
				std::cout << "\t" << opt.value() << std::endl;
			}
			else
			{
				// 指定した属性がなかったとき
				std::cout << "\t" << "属性なし" << std::endl;
			}

			// 属性id 取得
			if (auto opt = child.second.get_optional<std::string>("<xmlattr>.id"))
			{
				std::cout << "\t" << opt.value() << std::endl;
			}
            // elseは省略   

			// 要素の値
			if (auto opt = child.second.get_value_optional<std::string>())
			{
				std::cout << "\t" << opt.value() << std::endl;
			}
			
			// さらに子要素を取得			
			if (auto opt = child.second.get_child_optional(""))
			{
				opt.value().erase("<xmlattr>");		// 属性情報を削除
				for (auto& item : opt.value())
				{
					std::cout << "\t" << item.second.get_value<std::string>() << std::endl;
				}
			}
		}
	}
	else
	{
		std::cout << "<root>.<sample0>が存在しない." << std::endl;
	}
}
結果
child0
         color = yellow
         id = 0
         value = banana
child1
         color = red
         id = 1
         value = apple
child2
         color =
         id = 2
         value =
        melon
        water melon

property_tree::pathを使う方法

sample1.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <sample1>
    <child0>
      <child1>
        <child2>orange</child2>
      </child1>
    </child0>
  </sample1>
</root>
  • パスを連結する場合はproperty_tree::pathを使うとちょっと便利
  • std::stringでもできるけどセパレータを考慮する必要がある
コード
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

using namespace boost::property_tree;
using namespace boost::property_tree::xml_parser;

int main()
{	
	ptree pt;
	read_xml("sample1.xml", pt, trim_whitespace);

    // property_tree::pathを使う
	path p1 = "root.sample1.child0.child1"; 
	path p2 = "child2";
	path p3 = p1 /= p2;

	// これでもできるけど...
	//std::string p1 = "root.sample1.child0.child1";
	//std::string p2 = "child2";
	//std::string p3 = p1 + "." + p2;
 
	if (auto opt = pt.get_optional<std::string>(p3))
	{
		std::cout << opt.value() << std::endl;
	}
}
結果
orange

次にWriteしてみる

XMLはこんな感です。

sample2.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <sample2>
    <child0>melon</child0>
    <child1 color="red">apple</child1>
    <child2/>
  </sample2>
</root>
  • putが変更、addが追加
  • Writeするときに、xml_writer_make_settingsを渡すことでインデントやエンコーディングを指定できる
コード
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

using namespace boost::property_tree;
using namespace boost::property_tree::xml_parser;

int main()
{
	ptree pt;
	read_xml("sample2.xml", pt, trim_whitespace);	

	// 要素の値を変更
	pt.put("root.sample2.child0", "banana");
	
	// 要素の属性を変更
	pt.put("root.sample2.child1.<xmlattr>.color", "blue");
	
	if (auto opt = pt.get_child_optional("root.sample2"))
	{
		// 要素の削除
		opt.value().erase("child2");
	}
	
	if (auto opt = pt.get_child_optional("root.sample2"))
	{
		// 要素の追加
		 ptree& child = opt.value().add("child3", "lemon");

		 // 属性の追加
		 child.put("<xmlattr>.color", "yellow");
	}
 
	const int indent = 2;
	write_xml<ptree>("sample2.xml", pt, std::locale(),
		xml_writer_make_settings<std::string>(' ', indent));}

    // xml_writer_make_settingsを渡さないと...
	// write_xml<ptree>("sample2.xml", pt);

  
結果
<?xml version="1.0" encoding="utf-8"?>
<root>
  <sample2>
    <child0>banana</child0>
    <child1 color="blue">apple</child1>
    <child3 color="yellow">lemon</child3>
  </sample2>
</root>
結果(xml_writer_make_settingsなし)
<?xml version="1.0" encoding="utf-8"?>
<root><sample2><child0>banana</child0><child1 color="blue">apple</child1><child3 color="yellow">lemon</child3></sample2></root>

まとめ

昔(どの位昔かは秘密)はもっと苦労してRead/Writeしていたような気がしますが、とても簡単になりましたね。戻り値でboost::optionalが受け取れるのも使いやすいです。Boost Property Tree Libraryは、iniやjsonも扱えるようなので機会があれば使ってみたいです。

参考

Boost逆引きリファレンス XMLの読み込み/書き込み

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?