500 lines
14 KiB
C
500 lines
14 KiB
C
|
|
//
|
||
|
|
// Created by xkuang on 2022/10/24.
|
||
|
|
//
|
||
|
|
|
||
|
|
#ifndef HFFG_XML_PARSER_H
|
||
|
|
#define HFFG_XML_PARSER_H
|
||
|
|
|
||
|
|
#include <string>
|
||
|
|
#include <utility>
|
||
|
|
#include <vector>
|
||
|
|
#include <map>
|
||
|
|
#include <unordered_map>
|
||
|
|
#include <memory>
|
||
|
|
#include <fstream>
|
||
|
|
#include <iostream>
|
||
|
|
#include <sstream>
|
||
|
|
#include "rapidxml/rapidxml.hpp"
|
||
|
|
#include "rapidxml/rapidxml_utils.hpp"
|
||
|
|
#include "rapidxml/rapidxml_print.hpp"
|
||
|
|
|
||
|
|
template< typename T >
|
||
|
|
struct TypeIsString
|
||
|
|
{
|
||
|
|
static const bool value = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
template<>
|
||
|
|
struct TypeIsString< std::string >
|
||
|
|
{
|
||
|
|
static const bool value = true;
|
||
|
|
};
|
||
|
|
|
||
|
|
class XmlNode {
|
||
|
|
|
||
|
|
|
||
|
|
private:
|
||
|
|
XmlNode* root;
|
||
|
|
std::vector<char> m_buffer;
|
||
|
|
bool is_root;
|
||
|
|
rapidxml::xml_node<>* p_root;
|
||
|
|
rapidxml::xml_node<>* p_node;
|
||
|
|
std::vector< std::shared_ptr<rapidxml::xml_document<> > > copied_docs;
|
||
|
|
std::vector< std::shared_ptr<rapidxml::file<> > > copied_files;
|
||
|
|
|
||
|
|
std::shared_ptr<rapidxml::xml_document<>> doc;
|
||
|
|
std::shared_ptr<rapidxml::file<>> file;
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
//! construct an empty doc
|
||
|
|
XmlNode(){ is_root = false;};
|
||
|
|
|
||
|
|
XmlNode(const XmlNode& XmlNode)
|
||
|
|
{
|
||
|
|
p_node = XmlNode.p_node;
|
||
|
|
is_root = XmlNode.is_root;
|
||
|
|
p_root = XmlNode.p_root;
|
||
|
|
root = XmlNode.root;
|
||
|
|
doc = XmlNode.doc;
|
||
|
|
}
|
||
|
|
|
||
|
|
XmlNode(rapidxml::xml_node<>* node, rapidxml::xml_node<>* rapid_root,
|
||
|
|
std::shared_ptr<rapidxml::xml_document<>> ndoc,
|
||
|
|
std::shared_ptr<rapidxml::file<>> nfile
|
||
|
|
)
|
||
|
|
{
|
||
|
|
is_root = false;
|
||
|
|
if (node) {
|
||
|
|
p_node = node;
|
||
|
|
p_root = rapid_root;
|
||
|
|
doc = ndoc;
|
||
|
|
file = nfile;
|
||
|
|
root = this;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
std::cout << "xmlparser cannot get node" << std::endl;
|
||
|
|
if (rapid_root==node)
|
||
|
|
is_root = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
XmlNode(rapidxml::xml_node<>* node, rapidxml::xml_node<>* rapid_root,
|
||
|
|
std::shared_ptr<rapidxml::xml_document<>> ndoc,
|
||
|
|
std::shared_ptr<rapidxml::file<>> nfile, XmlNode* rt
|
||
|
|
)
|
||
|
|
{
|
||
|
|
is_root = false;
|
||
|
|
if (node) {
|
||
|
|
p_node = node;
|
||
|
|
p_root = rapid_root;
|
||
|
|
doc = ndoc;
|
||
|
|
file = nfile;
|
||
|
|
root = rt;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
std::cout << "xmlparser cannot get node" << std::endl;
|
||
|
|
if (rapid_root==node)
|
||
|
|
is_root = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//! load a root node (document) from stream
|
||
|
|
XmlNode(std::istream &in){
|
||
|
|
load(in);
|
||
|
|
}
|
||
|
|
|
||
|
|
///load a xml file from stream
|
||
|
|
void load(std::istream & in){
|
||
|
|
|
||
|
|
file = std::make_shared<rapidxml::file<>>(in);
|
||
|
|
|
||
|
|
doc = std::make_shared<rapidxml::xml_document<>>();
|
||
|
|
doc->parse<0>(file->data());
|
||
|
|
p_node = doc->first_node();
|
||
|
|
p_root = p_node;
|
||
|
|
is_root = true;
|
||
|
|
root = this;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! load a root node (document) from xml file
|
||
|
|
XmlNode(std::string filename){
|
||
|
|
load(filename);
|
||
|
|
}
|
||
|
|
///load a xml file
|
||
|
|
|
||
|
|
void load(const std::string & filename){
|
||
|
|
std::ifstream theFile (filename);
|
||
|
|
|
||
|
|
if (! theFile.is_open())
|
||
|
|
std::cerr << "cannot open xml file: " << filename << std::endl;
|
||
|
|
|
||
|
|
theFile.seekg(0, std::ios::end);
|
||
|
|
if (0 == theFile.tellg()) {
|
||
|
|
std::cerr << "xml file is empty" << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
file = std::make_shared<rapidxml::file<>>(filename.c_str());
|
||
|
|
doc = std::make_shared<rapidxml::xml_document<>>();
|
||
|
|
doc->parse<0>(file->data());
|
||
|
|
p_node = doc->first_node();
|
||
|
|
p_root = p_node;
|
||
|
|
root = this;
|
||
|
|
is_root = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
///close the xml file
|
||
|
|
void unload(){
|
||
|
|
;
|
||
|
|
}
|
||
|
|
//! assignment
|
||
|
|
XmlNode & operator= (const XmlNode & other)
|
||
|
|
{
|
||
|
|
if (this != &other) // protect against invalid self-assignment
|
||
|
|
{
|
||
|
|
// 1: allocate new memory and copy the elements
|
||
|
|
p_node = other.p_node;
|
||
|
|
is_root = other.is_root;
|
||
|
|
p_root = other.p_root;
|
||
|
|
doc = other.doc;
|
||
|
|
root = other.root;
|
||
|
|
file = other.file;
|
||
|
|
}
|
||
|
|
// by convention, always return *this
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get root node
|
||
|
|
XmlNode getRoot() const {
|
||
|
|
return XmlNode(p_root, p_root, doc, file);
|
||
|
|
};
|
||
|
|
|
||
|
|
//! get parent node, return NULL if this is root
|
||
|
|
XmlNode getParent() const {
|
||
|
|
if (is_root)
|
||
|
|
std::cerr << "failed to get parent of root node" << std::endl;
|
||
|
|
else
|
||
|
|
return XmlNode(p_node->parent(), p_root, doc, file);
|
||
|
|
};
|
||
|
|
|
||
|
|
//! whether the node has a child
|
||
|
|
bool hasChild(const std::string & childid) const{
|
||
|
|
bool is_child_exist;
|
||
|
|
if (childid=="")
|
||
|
|
is_child_exist = p_node->first_node();
|
||
|
|
else
|
||
|
|
is_child_exist = p_node->first_node(childid.c_str());
|
||
|
|
|
||
|
|
if (is_child_exist)
|
||
|
|
return true;
|
||
|
|
else
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get the first child node
|
||
|
|
XmlNode getChild(const std::string& child_id="") const{
|
||
|
|
|
||
|
|
if (!hasChild(child_id))
|
||
|
|
std::cerr << "failed to get child:" << child_id << std::endl;
|
||
|
|
|
||
|
|
if (child_id.empty())
|
||
|
|
return XmlNode(p_node->first_node(), p_root, doc, file, root);
|
||
|
|
else
|
||
|
|
return XmlNode(p_node->first_node(child_id.c_str()), p_root, doc, file, root);
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get all children, matching node name
|
||
|
|
std::vector<XmlNode> getChildren(std::string nodename) const {
|
||
|
|
std::vector<XmlNode> children;
|
||
|
|
auto child = p_node->first_node(nodename.c_str());
|
||
|
|
while (child) {
|
||
|
|
children.emplace_back(child, p_root, doc, file, root);
|
||
|
|
child = child->next_sibling(nodename.c_str());
|
||
|
|
}
|
||
|
|
return children;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get all children
|
||
|
|
std::vector<XmlNode> getChildren() const {
|
||
|
|
std::vector<XmlNode> children;
|
||
|
|
auto child = p_node->first_node();
|
||
|
|
while (child) {
|
||
|
|
children.emplace_back(child, p_root, doc, file, root);
|
||
|
|
child = child->next_sibling();
|
||
|
|
}
|
||
|
|
return children;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get all attributes: key is attribute name, value is attribute value
|
||
|
|
std::unordered_map<std::string, std::string> getAllAttr() const{
|
||
|
|
std::unordered_map<std::string, std::string> attributes;
|
||
|
|
auto attr = p_node->first_attribute();
|
||
|
|
while (attr) {
|
||
|
|
attributes[attr->name()] = attr->value();
|
||
|
|
attr = attr->next_attribute();
|
||
|
|
}
|
||
|
|
return attributes;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get all attributes: key is attribute name, value is attribute value
|
||
|
|
std::map<std::string, std::string> getAllAttrMap() const{
|
||
|
|
std::map<std::string, std::string> attributes;
|
||
|
|
auto attr = p_node->first_attribute();
|
||
|
|
while (attr) {
|
||
|
|
attributes[attr->name()] = attr->value();
|
||
|
|
attr = attr->next_attribute();
|
||
|
|
}
|
||
|
|
return attributes;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get a string. default is ""
|
||
|
|
std::string getAttrOrThrow(const std::string & key) const {
|
||
|
|
auto attr = p_node->first_attribute(key.c_str());
|
||
|
|
if (attr)
|
||
|
|
return std::string(attr->value());
|
||
|
|
else
|
||
|
|
std::cerr << "getAttrOrThrow cannot find key=" << key << std::endl;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get a string. default is ""
|
||
|
|
std::string getAttrString(const std::string & key) const {
|
||
|
|
auto attr = p_node->first_attribute(key.c_str());
|
||
|
|
if (attr)
|
||
|
|
return std::string(attr->value());
|
||
|
|
else
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string print() const
|
||
|
|
{
|
||
|
|
std::stringstream ss;
|
||
|
|
ss<<*p_node;
|
||
|
|
return ss.str();
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
//! get a string 1
|
||
|
|
std::string getAttrDefault(const std::string & key, const char* def_value) const{
|
||
|
|
auto attr = p_node->first_attribute(key.c_str());
|
||
|
|
std::string ans, ret;
|
||
|
|
if (attr)
|
||
|
|
ans = std::string(attr->value());
|
||
|
|
else
|
||
|
|
return def_value;
|
||
|
|
return ans;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get a string 2
|
||
|
|
std::string getAttrDefault(const std::string & key, const std::string def_value) const{
|
||
|
|
return getAttrDefault(key, def_value.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get a string 3
|
||
|
|
std::string getAttrDefault(const std::string & key, char* def_value) const{
|
||
|
|
return getAttrDefault(key, const_cast<const char*>(def_value));
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get an integer
|
||
|
|
int getAttrDefault(const std::string & key, int def_value) const
|
||
|
|
{
|
||
|
|
auto attr = p_node->first_attribute(key.c_str());
|
||
|
|
std::string ret;
|
||
|
|
if (attr)
|
||
|
|
ret = std::string(attr->value());
|
||
|
|
else
|
||
|
|
return def_value;
|
||
|
|
return atoi(ret.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get a bool
|
||
|
|
bool getAttrDefault(const std::string & key, bool def_value) const{
|
||
|
|
auto attr = p_node->first_attribute(key.c_str());
|
||
|
|
std::string ans, ret;
|
||
|
|
if ( !attr ) {
|
||
|
|
return def_value;
|
||
|
|
}
|
||
|
|
if ( std::string(attr->value()) == "true")
|
||
|
|
return true;
|
||
|
|
else if ( std::string(attr->value())=="false" )
|
||
|
|
return false;
|
||
|
|
else
|
||
|
|
return def_value;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
//! get a float
|
||
|
|
float getAttrDefault(const std::string & key, float def_value) const{
|
||
|
|
auto attr = p_node->first_attribute(key.c_str());
|
||
|
|
std::string ret;
|
||
|
|
if (attr)
|
||
|
|
ret = std::string(attr->value());
|
||
|
|
else
|
||
|
|
return def_value;
|
||
|
|
return atof(ret.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get node name
|
||
|
|
std::string getNodeName() const{
|
||
|
|
return p_node->name();
|
||
|
|
}
|
||
|
|
|
||
|
|
//! get node value
|
||
|
|
std::string getNodeValue() const {
|
||
|
|
return p_node->value();
|
||
|
|
}
|
||
|
|
//! get value string eg: root.cfg1..
|
||
|
|
std::string getValueDefault(const std::string & key, const char* def_value) const {
|
||
|
|
unsigned pos1 = 0, pos2 = 0;
|
||
|
|
std::string sub_key;
|
||
|
|
pos1 = key.find_first_of('.');
|
||
|
|
XmlNode pNode = *this;
|
||
|
|
while (pos1 != std::string::npos) {
|
||
|
|
sub_key = key.substr(pos2, pos1 - pos2);
|
||
|
|
if (sub_key.compare(getNodeName()) == 0) {
|
||
|
|
pos2 = pos1 + 1;
|
||
|
|
pos1 = key.find_first_of('.', pos2);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (pNode.hasChild(sub_key)){
|
||
|
|
pNode = pNode.getChild(sub_key);
|
||
|
|
}
|
||
|
|
else{
|
||
|
|
return def_value;
|
||
|
|
}
|
||
|
|
pos2 = pos1 + 1;
|
||
|
|
pos1 = key.find_first_of('.', pos2);
|
||
|
|
}
|
||
|
|
if (pos2 != key.length()) {
|
||
|
|
sub_key = key.substr(pos2);
|
||
|
|
if (pNode.hasChild(sub_key)) {
|
||
|
|
pNode = pNode.getChild(sub_key);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return def_value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return pNode.getNodeValue();
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string getValueDefault(const std::string & key, const std::string &def_value) const {
|
||
|
|
return getValueDefault(key, def_value.c_str());
|
||
|
|
}
|
||
|
|
|
||
|
|
long getValueDefault(const std::string & key, long def_value) {
|
||
|
|
std::string value = getValueDefault(key, "");
|
||
|
|
if (value.empty()) {
|
||
|
|
return def_value;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return atol(value.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int getValueDefault(const std::string & key, int def_value) {
|
||
|
|
std::string value = getValueDefault(key, "");
|
||
|
|
if (value.empty()) {
|
||
|
|
return def_value;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return atoi(value.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
double getValueDefault(const std::string & key, double def_value) {
|
||
|
|
std::string value = getValueDefault(key, "");
|
||
|
|
if (value.empty()) {
|
||
|
|
return def_value;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
return atof(value.c_str());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//! is root node
|
||
|
|
bool isRoot() const {
|
||
|
|
return is_root;
|
||
|
|
}
|
||
|
|
|
||
|
|
//! append a child node to the current node. return appended child
|
||
|
|
XmlNode appendChild(const std::string & nodename)
|
||
|
|
{
|
||
|
|
char *node_name = doc->allocate_string(nodename.c_str());
|
||
|
|
auto node = doc->allocate_node(rapidxml::node_element, node_name);
|
||
|
|
p_node->append_node(node);
|
||
|
|
return XmlNode(node, p_root, doc, file, root);
|
||
|
|
}
|
||
|
|
|
||
|
|
XmlNode appendChild(XmlNode & new_node)
|
||
|
|
{
|
||
|
|
auto node = doc->clone_node( new_node.p_node );
|
||
|
|
p_node->append_node(node);
|
||
|
|
copied_docs.push_back(new_node.doc);
|
||
|
|
copied_files.push_back(new_node.file);
|
||
|
|
return XmlNode(node, p_root, doc, file, root);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
XmlNode appendChild(XmlNode & new_node, XmlNode & rt)
|
||
|
|
{
|
||
|
|
auto node = doc->clone_node( new_node.p_node );
|
||
|
|
p_node->append_node(node);
|
||
|
|
rt.copied_docs.push_back(new_node.doc);
|
||
|
|
rt.copied_files.push_back(new_node.file);
|
||
|
|
return XmlNode(node, p_root, doc, file, &rt);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
template<typename MDType>
|
||
|
|
MDType appendAttr(const std::string & key, const MDType value) const{
|
||
|
|
//char* rapidAttributeName = doc.allocate_string(key.c_str());
|
||
|
|
auto attr = p_node->first_attribute(key.c_str());
|
||
|
|
std::string ret;
|
||
|
|
MDType a = {0};
|
||
|
|
if (attr)
|
||
|
|
ret = std::string(attr->value());
|
||
|
|
else
|
||
|
|
return def_value;
|
||
|
|
std::istringstream ss(ret);
|
||
|
|
ss >> a;
|
||
|
|
return a;
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
|
||
|
|
//! append attribute to current node
|
||
|
|
|
||
|
|
void appendAttr(const std::string & name, const char * value)
|
||
|
|
{
|
||
|
|
auto attr = p_node->first_attribute(name.c_str());
|
||
|
|
char *new_value = doc->allocate_string(value);
|
||
|
|
if (attr)
|
||
|
|
{
|
||
|
|
attr->value(new_value);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
char *new_name = doc->allocate_string(name.c_str());
|
||
|
|
auto attr = doc->allocate_attribute(new_name,new_value);
|
||
|
|
p_node->append_attribute(attr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void appendAttr(const std::string & name, const std::string& value){
|
||
|
|
return appendAttr(name, value.c_str());
|
||
|
|
}
|
||
|
|
void appendAttr(const std::string & name, float value){
|
||
|
|
return appendAttr(name, std::to_string( (long double)value));
|
||
|
|
}
|
||
|
|
|
||
|
|
void appendAttr(const std::string & name, int value){
|
||
|
|
return appendAttr(name, std::to_string( (long long)value));
|
||
|
|
}
|
||
|
|
|
||
|
|
void appendAttr(const std::string & name, bool value){
|
||
|
|
if (value)
|
||
|
|
appendAttr(name, "true");
|
||
|
|
else
|
||
|
|
appendAttr(name, "false");
|
||
|
|
}
|
||
|
|
};
|
||
|
|
#endif //HFFG_XML_PARSER_H
|