功能很简陋, 下面两个无法解析。
<?xml version="1.0" encoding="UTF-8"?>
<xxx/>
能解析的格式类似于下面:
<UI_head>
<Button>Button1</Button>
<Button>Button2</Button>
<Grid>
<Button color ="white" x = "234" y = "456" isShow = "true">Grid.Button1</Button>
<Button>Grid.Button2</Button>
<!-- 这是一个按钮 -->
</Grid>
</UI_head>
效果:
源码:
Value.h
#pragma once
#include <string>
#include <sstream>
namespace lakes {
class Value
{
public:
Value() { }
Value(const std::string& value);
Value(const char * value);
Value(double value);
Value(int value);
Value& operator=(double value);
Value& operator=(int value);
Value& operator=(bool value);
Value& operator=(const std::string& value);
bool operator ==(const Value& other);
bool operator !=(const Value& other);
Value(bool value);
operator bool();
operator int();
operator double();
operator const char*();
operator std::string();
std::string& str() {
return _str_value;
}
private:
std::string _str_value;
};
}
Value.cpp
#include "Value.h"
lakes::Value::Value(const std::string& value) {
_str_value = value;
}
lakes::Value::Value(const char* value) {
_str_value = value;
}
lakes::Value::Value(double value) {
std::stringstream buf;
buf << value;
_str_value = buf.str();
}
lakes::Value::Value(int value) {
_str_value = value;
}
lakes::Value& lakes::Value::operator=(double value) {
std::stringstream buf;
buf << value;
_str_value = buf.str();
return *this;
}
lakes::Value& lakes::Value::operator=(int value) {
_str_value = value;
return *this;
}
lakes::Value& lakes::Value::operator=(bool value) {
_str_value = value;
return *this;
}
lakes::Value& lakes::Value::operator=(std::string& value) {
_str_value = value;
return *this;
}
bool lakes::Value::operator==(const Value& other) {
return other._str_value == _str_value;
}
bool lakes::Value::operator!=(const Value& other) {
return other._str_value != _str_value;
}
lakes::Value::Value(bool value) {
*this = value;
}
lakes::Value::operator bool() {
return _str_value == "true";
}
lakes::Value::operator int() {
return std::atoi(_str_value.c_str());
}
lakes::Value::operator double() {
return std::atof(_str_value.c_str());
}
lakes::Value::operator const char* () {
return _str_value.c_str();
}
lakes::Value::operator std::string() {
return _str_value;
}
Xml.h
#pragma once
#include <string>
#include <list>
#include <map>
#include <stack>
#include <algorithm>
#include <fstream>
#include <sstream>
#include "Value.h"
namespace lakes {
/**
* <Parent1>
* <Node1 xx_property = "xxx"> xxx_str </Node1>
* <Node2 xx_property = "xxx"> xxx_str </Node2>
* <Node3> <Node>xxx</Node> </Node3>
* </Parent1>
* ...
*/
class Xml {
using itor = std::list<Xml>::iterator;
public:
Xml() { }
Xml(const std::string& path) {
load(path.c_str());
}
void load(const char* path);
bool save(const std::string& path);
void parse(const std::string& str);
std::string name() const;
void name(const std::string& str);
std::string text() const;
void text(const std::string& str);
Value attr(const std::string& str_name) const;
void attr(const std::string& str_name, const std::string& str_attr);
Value operator[](const std::string& str_attr);
Xml& find(const std::string& str);
Xml& append(const Xml& xml_node);
std::list<Xml>& childs();
std::string to_string();
itor begin();
itor end();
void clear();
private:
std::string _name;
std::string _text;
std::map<std::string, Value> _attrs;
std::list<Xml> _childs;
char _get_not_space_char(const std::string& str, int& index);
void _parse_text(Xml* node, const std::string& str, int& index);
void _parse_attr(Xml& node, const std::string& str, int& index);
Xml _parse_node(const std::string& str, int& index);
void _end_node(std::stack<Xml*>& node_stack, const std::string& str, int& index);
};
} // namespace lake
Xml.cpp
#include "Xml.h"
#define THROW_ERROR(...) do { \
char str_err[256] { }; \
sprintf_s(str_err, __VA_ARGS__); \
throw std::logic_error(str_err); \
}while(0);
void lakes::Xml::load(const char* path) {
std::ifstream file(path, std::ios_base::in);
file.open(path);
if (!file.is_open()) {
THROW_ERROR("Open file error...");
}
std::stringstream str_stream;
str_stream << file.rdbuf();
parse(str_stream.str());
}
bool lakes::Xml::save(const std::string& path) {
std::ofstream file(path, std::ios_base::out);
file.open(path);
if (!file.is_open()) {
return false;
}
file << to_string();
return true;
}
void lakes::Xml::parse(const std::string& str) {
size_t text_len = str.length();
if (text_len == 0) {
THROW_ERROR("string is empty...");
}
std::stack<Xml*> node_stack;
int index = 0;
while (index < text_len - 1) {
char ch = _get_not_space_char(str, index);
if (ch == '<') {
index++;
if (str[index] == '/') {
index++;
_end_node(node_stack, str, index);
}
else if (!str.compare(index, 3, "!--")) { //skip <!-- xxx -->
int ind = (int)str.find("-->", index);
if (ind == std::string::npos) {
THROW_ERROR("error: %d could not match -->", index);
}
index = ind + 3;
}
else {
auto node = _parse_node(str, index);
if (node_stack.empty()) {
*this = node;
node_stack.push(this);
}
else {
node_stack.top()->append(node);
node_stack.push(&node_stack.top()->childs().back());
}
}
}
else {
_parse_text(node_stack.top(), str, index);
}
}
if (!node_stack.empty()) {
THROW_ERROR("error: root node couldn't match...");
}
}
std::string lakes::Xml::name() const {
return _name;
}
void lakes::Xml::name(const std::string& str) {
_name = str;
}
std::string lakes::Xml::text() const {
return _text;
}
void lakes::Xml::text(const std::string& str) {
_text = str;
}
lakes::Value lakes::Xml::attr(const std::string& str_name) const {
if (_attrs.count(str_name) == 0) {
return "None";
}
return _attrs.at(str_name);
}
void lakes::Xml::attr(const std::string& str_name, const std::string& str_attr) {
_attrs[str_name] = str_attr;
}
lakes::Xml& lakes::Xml::find(const std::string& str) {
const auto& node = std::find_if(_childs.begin(), _childs.end(), [&str](Xml& xml_node) {
return (xml_node.name() == str);
});
if (_childs.end() == node) {
THROW_ERROR("Not Find...");
}
return *node;
}
lakes::Value lakes::Xml::operator[](const std::string& str_attr) {
return attr(str_attr);
}
lakes::Xml& lakes::Xml::append(const Xml& xml_node) {
_childs.emplace_back(xml_node);
return *this;
}
std::list<lakes::Xml>& lakes::Xml::childs() {
return _childs;
}
std::string lakes::Xml::to_string() {
std::string xml_str;
xml_str = "<" + _name;
for (auto& value : _attrs) {
xml_str += (" " + value.first + "=\"" + value.second.str() + "\"");
}
xml_str += ">" + _text;
for (auto& value : _childs) {
xml_str += value.to_string();
}
xml_str += "</" + _name + ">";
return xml_str;
}
lakes::Xml::itor lakes::Xml::begin() {
return _childs.begin();
}
lakes::Xml::itor lakes::Xml::end() {
return _childs.end();
}
void lakes::Xml::clear() {
_attrs.clear();
_childs.clear();
}
char lakes::Xml::_get_not_space_char(const std::string& str, int& index) {
while (index < str.length() && (str[index] == ' ' || str[index] == '\n')) {
index++;
}
return str[index];
}
void lakes::Xml::_parse_text(Xml* node, const std::string& str, int& index) {
while (index < str.length() && str[index] != '<' && str[index] != '\n') {
node->_text += str[index++];
}
}
void lakes::Xml::_parse_attr(Xml& node, const std::string& str, int& index) {
std::string attr_name;
std::string attr_val;
int flag = 1;
while (index < str.length()) {
char ch = str[index];
if (ch == '>') {
index++;
break;
}
else if ((ch == '=' || ch == ' ') && flag == 1) {
flag = -1;
if (ch == ' ') {
ch = _get_not_space_char(str, index);
if (ch != '=') {
THROW_ERROR("error : %d not \'=\'...", index);
}
}
index++;
ch = _get_not_space_char(str, index);
if (ch != '\"') {
THROW_ERROR("error: %d not \" ...", index);
}
}
else if (ch == '\"') {
flag = 1;
node.attr(attr_name, attr_val);
attr_name.clear();
attr_val.clear();
while (index + 1 < str.length() && (str[++index] == ' ' || str[index] == '\n' || str[index] == '\r')) {}
continue;
}
else if (ch != '\n' && ch != '\r') {
if (flag == 1) {
attr_name += ch;
}
else {
attr_val += ch;
}
}
index++;
}
}
lakes::Xml lakes::Xml::_parse_node(const std::string& str, int& index) {
char ch = _get_not_space_char(str, index);
Xml xml_node;
std::string str_temp;
while (++index < str.length()) {
if (ch == '>' || ch == ' ' || ch == '\n')
break;
xml_node._name += ch;
ch = str[index];
}
if (ch != '>') {
_parse_attr(xml_node, str, index);
}
return xml_node;
}
void lakes::Xml::_end_node(std::stack<Xml*>& node_stack, const std::string& str, int& index) {
char ch;
std::string temp_buf;
while (index < str.length()) {
ch = _get_not_space_char(str, index);
index++;
if (ch == '>')
break;
temp_buf += ch;
}
if (temp_buf == node_stack.top()->name()) {
node_stack.pop();
}
else {
THROW_ERROR("error: %d : %s != %s", index, node_stack.top()->name().c_str(), temp_buf.c_str());
}
}