Boostライブラリー(バージョン1.52.0)を使用してC++でXML要素からのいくつかの属性を処理しようとしているときに発生している問題を共有したいと思います。次のコードがあるとします:
#define ATTR_SET ".<xmlattr>"
#define XML_PATH1 "./pets.xml"
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
using namespace std;
using namespace boost;
using namespace boost::property_tree;
const ptree& empty_ptree(){
static ptree t;
return t;
}
int main() {
ptree tree;
read_xml(XML_PATH1, tree);
const ptree & formats = tree.get_child("pets", empty_ptree());
BOOST_FOREACH(const ptree::value_type & f, formats){
string at = f.first + ATTR_SET;
const ptree & attributes = formats.get_child(at, empty_ptree());
cout << "Extracting attributes from " << at << ":" << endl;
BOOST_FOREACH(const ptree::value_type &v, attributes){
cout << "First: " << v.first.data() << " Second: " << v.second.data() << endl;
}
}
}
次のXML構造があるとします。
<?xml version="1.0" encoding="utf-8"?>
<pets>
<cat name="Garfield" weight="4Kg">
<somestuff/>
</cat>
<dog name="Milu" weight="7Kg">
<somestuff/>
</dog>
<bird name="Tweety" weight="0.1Kg">
<somestuff/>
</bird>
</pets>
したがって、次のコンソール出力が得られます。
Extracting attributes from cat.<xmlattr>:
First: name Second: Garfield
First: weight Second: 4Kg
Extracting attributes from dog.<xmlattr>:
First: name Second: Milu
First: weight Second: 7Kg
Extracting attributes from bird.<xmlattr>:
First: name Second: Tweety
First: weight Second: 0.1Kg
ただし、ルートノードから配置されたすべての要素に共通の構造を使用すると(それらを特定の属性から識別するため)、結果は完全に変化します。このような場合、これはXMLファイルである可能性があります。
<?xml version="1.0" encoding="utf-8"?>
<pets>
<pet type="cat" name="Garfield" weight="4Kg">
<somestuff/>
</pet>
<pet type="dog" name="Milu" weight="7Kg">
<somestuff/>
</pet>
<pet type="bird" name="Tweety" weight="0.1Kg">
<somestuff/>
</pet>
</pets>
そして、出力は次のようになります:
Extracting attributes from pet.<xmlattr>:
First: type Second: cat
First: name Second: Garfield
First: weight Second: 4Kg
Extracting attributes from pet.<xmlattr>:
First: type Second: cat
First: name Second: Garfield
First: weight Second: 4Kg
Extracting attributes from pet.<xmlattr>:
First: type Second: cat
First: name Second: Garfield
First: weight Second: 4Kg
3セットの属性が出力されているため、ルートノードからぶら下がっている要素の数が正しく認識されているようです。それにもかかわらず、それらはすべて、最初の要素の属性を参照しています...
私はC++のエキスパートではなく、Boostを初めて使用するので、これはハッシュマッピング処理などに関して欠けているものかもしれません...アドバイスがあれば大歓迎です。
プログラムの問題は次の行にあります。
const ptree & attributes = formats.get_child(at, empty_ptree());
この行を使用して、子を取得するように求めていますpet.<xmlattr>
from pets
そして、トラバースしているf
に関係なく、これを3回実行します。以下 この記事 使用する必要があるのは次のとおりだと思います:
const ptree & attributes = f.second.get_child("<xmlattr>", empty_ptree());
両方のxmlファイルで機能する完全なコードは次のとおりです。
#define ATTR_SET ".<xmlattr>"
#define XML_PATH1 "./pets.xml"
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
using namespace std;
using namespace boost;
using namespace boost::property_tree;
const ptree& empty_ptree(){
static ptree t;
return t;
}
int main() {
ptree tree;
read_xml(XML_PATH1, tree);
const ptree & formats = tree.get_child("pets", empty_ptree());
BOOST_FOREACH(const ptree::value_type & f, formats){
string at = f.first + ATTR_SET;
const ptree & attributes = f.second.get_child("<xmlattr>", empty_ptree());
cout << "Extracting attributes from " << at << ":" << endl;
BOOST_FOREACH(const ptree::value_type &v, attributes){
cout << "First: " << v.first.data() << " Second: " << v.second.data() << endl;
}
}
}
これまでこの機能を使用していなかったとしたら、boost::property_tree
XMLパーサーは一般的なXMLパーサーではないのではないかと思いますが、特定のスキーマに1つの特定のプロパティに対して1つの特定のタグがあることを期待しています。
boost::property_tree
機能を超えてXMLを操作する場合は、XMLスキーマの解析を提供する他のXMLパーサーを使用することをお勧めします。例を見てください。 Xerces C++ または Poco XML 。
解析するファイル、pets.xml
<pets>
<pet type="cat" name="Garfield" weight="4Kg">
<something name="test" value="*"/>
<something name="demo" value="@"/>
</pet>
<pet type="dog" name="Milu" weight="7Kg">
<something name="test1" value="$"/>
</pet>
<birds type="parrot">
<bird name="african grey parrot"/>
<bird name="Amazon parrot"/>
</birds>
</pets>
コード:
// DemoPropertyTree.cpp : Defines the entry point for the console application.
//Prerequisite boost library
#include "stdafx.h"
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/foreach.hpp>
#include<iostream>
using namespace std;
using namespace boost;
using namespace boost::property_tree;
void processPet(ptree subtree)
{
BOOST_FOREACH(ptree::value_type petChild,subtree.get_child(""))
{
//processing attributes of element pet
if(petChild.first=="<xmlattr>")
{
BOOST_FOREACH(ptree::value_type petAttr,petChild.second.get_child(""))
{
cout<<petAttr.first<<"="<<petAttr.second.data()<<endl;
}
}
//processing child element of pet(something)
else if(petChild.first=="something")
{
BOOST_FOREACH(ptree::value_type somethingChild,petChild.second.get_child(""))
{
//processing attributes of element something
if(somethingChild.first=="<xmlattr>")
{
BOOST_FOREACH(ptree::value_type somethingAttr,somethingChild.second.get_child(""))
{
cout<<somethingAttr.first<<"="<<somethingAttr.second.data()<<endl;
}
}
}
}
}
}
void processBirds(ptree subtree)
{
BOOST_FOREACH(ptree::value_type birdsChild,subtree.get_child(""))
{
//processing attributes of element birds
if(birdsChild.first=="<xmlattr>")
{
BOOST_FOREACH(ptree::value_type birdsAttr,birdsChild.second.get_child(""))
{
cout<<birdsAttr.first<<"="<<birdsAttr.second.data()<<endl;
}
}
//processing child element of birds(bird)
else if(birdsChild.first=="bird")
{
BOOST_FOREACH(ptree::value_type birdChild,birdsChild.second.get_child(""))
{
//processing attributes of element bird
if(birdChild.first=="<xmlattr>")
{
BOOST_FOREACH(ptree::value_type birdAttr,birdChild.second.get_child(""))
{
cout<<birdAttr.first<<"="<<birdAttr.second.data()<<endl;
}
}
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
const std::string XML_PATH1 = "C:/Users/10871/Desktop/pets.xml";
ptree pt1;
boost::property_tree::read_xml( XML_PATH1, pt1 );
cout<<"********************************************"<<endl;
BOOST_FOREACH( ptree::value_type const& topNodeChild, pt1.get_child( "pets" ) )
{
ptree subtree = topNodeChild.second;
if( topNodeChild.first == "pet" )
{
processPet(subtree);
cout<<"********************************************"<<endl;
}
else if(topNodeChild.first=="birds")
{
processBirds(subtree);
cout<<"********************************************"<<endl;
}
}
getchar();
return 0;
}
出力を次に示します。 output