根据proto文件配合tinyxml2自动解析xml

使用Xml文件每次解析起来都很麻烦,一直想实现一个根据配置自动解析Xml的功能,迟迟没有好的方案,正好最近在看Protobuf相关的知识,发现Protobuf的一些特性可以用来实现自动解析Xml,便使用Protobuf的相关特性配合tinyxml2,根据proto配置文件实现了Xml的自动解析功能。

自动解析的实现流程:

1.根据xml文件配置proto文件 .proto文件是什么

    school.xml文件内容:

根据xml文件配置的proto文件:

syntax = 'proto3';
package load;
import "google/protobuf/descriptor.proto";

message LoadSet{
    string file = 1;           // 文件名
    string msg = 2;            // 对应的消息结构名
}

extend google.protobuf.EnumValueOptions{
    LoadSet loadset=1000;
}

enum XmlInfo {
    Xml_none = 0;
    Xml_school = 1 [(loadset) = {file:'school.xml' msg:'SchoolConf'}];
}

message SchoolConf{
    SchoolAtr attr = 1;
    repeated SchoolSudent student = 2;       
}

message SchoolAtr{
    string name = 1;                //学校名称
    int32 student_num = 2;          //学生数量
    float grade = 3;                //学校评分
}

message SchoolSudent{
    int32 id = 1;               //学生id
    string name = 2;            //学生名称
    repeated int32 scores = 3;  //成绩集合,语文,数学,英语 (逗号分隔成绩)
}

在proto文件里定义了需要解析的的文件名称和对应的映射Message(SchoolConf),文件的SchoolConf又包含SchoolAtr 和SchoolSudent ......,可以看到proto文件和xml文件的结构是对应的(Message 对应ElementNode 基本类型(int,float...)字段对应 TextNode)。

2.代码实现:

直接上代码,如下

xmlProto.h文件:


#ifndef __xmlProto__
#define __xmlProto__

#include <tinyxml2/tinyxml2.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/text_format.h>
#include "loadConf.pb.h"
#include <map>
#include <vector>
#include <string>

using namespace ::google::protobuf;

class xmlProto
{
public:
	xmlProto();
	~xmlProto();

	void loadXml();
	void printProtoXml();
protected:
	bool loadXml(const std::string &fileName, Message *xmlMsg);
	bool loadXml(const tinyxml2::XMLElement *element, Message *xmlMsg);
	bool parseXml(const tinyxml2::XMLElement *element, const FieldDescriptor* fileDes, const Reflection* rel, Message *xmlMsg);
	bool parseRepeatedXml(const tinyxml2::XMLElement *element, const tinyxml2::XMLAttribute *attriNode, const FieldDescriptor* field, const Reflection* rel, Message *xmlMsg);
	void parseRepeatedString(const FieldDescriptor* field, const Reflection* rel, Message *xmlMsg, const std::string &content);

	std::string getFileData(const string &fileName);
	void splitStr(const std::string &content, std::vector<std::string> &strVec);
private:
	std::map<int, Message *> m_cacheMap;
};

#endif

xmlProto.cpp文件


#include "xmlProto.h"
#include <iostream>
#include <direct.h>

void xmlLoadLog(const char *arg)
{
	std::cout << arg << std::endl;
}

void xmlLoadLog(const std::string &str)
{
	xmlLoadLog(str.c_str());
}

void xmlLoadLog(const int v)
{
	std::cout << v << std::endl;
}

xmlProto::xmlProto()
{
}

xmlProto::~xmlProto()
{
}

void xmlProto::loadXml()
{
	const EnumDescriptor *enumDes = DescriptorPool::generated_pool()->FindEnumTypeByName("load.XmlInfo");
	//find load xmls setting info
	if (enumDes)
	{
		for (size_t i = 0; i < enumDes->value_count(); i++)
		{
			const EnumValueDescriptor *enumValueDes = enumDes->value(i);
			const load::LoadSet lset = enumValueDes->options().GetExtension(load::loadset);
			//get options
			if (!lset.file().empty())
			{
				const Descriptor *xmlDes = DescriptorPool::generated_pool()->FindMessageTypeByName("load." + lset.msg());
				if (xmlDes)
				{
					Message *xmlMsg = MessageFactory::generated_factory()->GetPrototype(xmlDes)->New();
					this->loadXml(lset.file(), xmlMsg);
					m_cacheMap[enumValueDes->number()] = xmlMsg;
				}
			}
		}
	}
}

void xmlProto::printProtoXml()
{
	auto it_f = m_cacheMap.find(load::Xml_school);
	if (it_f != m_cacheMap.end())
	{
		std::string str;
		//serialize message
		TextFormat::PrintToString(*it_f->second, &str);
		xmlLoadLog(str);
	}
}

bool xmlProto::loadXml(const string &fileName, Message *xmlMsg)
{
	auto data = this->getFileData(fileName);
	xmlLoadLog(data);

	tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument;
	if (doc->Parse(data.c_str(), data.size()) != tinyxml2::XML_SUCCESS)
	{
		xmlLoadLog("parse " + fileName + " falied");
		return false;
	}
	const tinyxml2::XMLElement* rootEle = doc->RootElement();
	if (!rootEle){
		return true;
	}
	return loadXml(rootEle, xmlMsg);
}

bool xmlProto::loadXml(const tinyxml2::XMLElement *element, Message *xmlMsg)
{
	const Descriptor* des = xmlMsg->GetDescriptor();
	const Reflection* rel = xmlMsg->GetReflection();
	for (int i = 0; i < des->field_count(); i++)
	{
		auto filed = des->field(i);
		parseXml(element, filed, rel, xmlMsg);
	}
	return true;
}

bool xmlProto::parseXml(const tinyxml2::XMLElement *element, const FieldDescriptor* field, const Reflection* rel, Message *xmlMsg)
{
	const tinyxml2::XMLAttribute *attriNode = nullptr;
	if (field->cpp_type() != FieldDescriptor::CppType::CPPTYPE_MESSAGE){
		attriNode = element->FindAttribute(field->name().c_str());
		if (!attriNode || std::string(attriNode->Value()).empty())
		{
			return true;
		}
	}

	if (field->is_repeated()){
		// load repeated
		return parseRepeatedXml(element, attriNode, field, rel, xmlMsg);
	}

	switch (field->cpp_type())
	{
	case FieldDescriptor::CppType::CPPTYPE_INT32:
		rel->SetInt32(xmlMsg, field, attriNode->IntValue());
		break;
	case FieldDescriptor::CppType::CPPTYPE_INT64:
		rel->SetInt64(xmlMsg, field, attriNode->Int64Value());
		break;
	case FieldDescriptor::CppType::CPPTYPE_UINT32:
		rel->SetUInt32(xmlMsg, field, attriNode->IntValue());
		break;
	case FieldDescriptor::CppType::CPPTYPE_UINT64:
		rel->SetUInt64(xmlMsg, field, attriNode->Int64Value());
		break;
	case FieldDescriptor::CppType::CPPTYPE_DOUBLE:
		rel->SetDouble(xmlMsg, field, attriNode->DoubleValue());
		break;
	case FieldDescriptor::CppType::CPPTYPE_FLOAT:
		rel->SetFloat(xmlMsg, field, attriNode->FloatValue());
		break;
	case FieldDescriptor::CppType::CPPTYPE_BOOL:
		rel->SetBool(xmlMsg, field, (bool)attriNode->IntValue());
		break;
	case FieldDescriptor::CppType::CPPTYPE_ENUM:
		//-------
		break;
	case FieldDescriptor::CppType::CPPTYPE_STRING:
		rel->SetString(xmlMsg, field, attriNode->Value());
		break;
	case FieldDescriptor::CppType::CPPTYPE_MESSAGE:
	{
													  auto ele = element->FirstChildElement(field->name().c_str());
													  if (ele)
													  {
														  Message *newMsg = rel->MutableMessage(xmlMsg, field);
														  return loadXml(ele, newMsg);
													  }
	}
		break;
	default:
		break;
	}
	return true;
}

bool xmlProto::parseRepeatedXml(const tinyxml2::XMLElement *element, const tinyxml2::XMLAttribute *attriNode, const FieldDescriptor* field, const Reflection* rel, Message *xmlMsg)
{
	switch (field->cpp_type())
	{
	case FieldDescriptor::CppType::CPPTYPE_MESSAGE:
	{
		auto ele = element->FirstChildElement(field->name().c_str());
		while (ele)
		{
			Message *newMsg = rel->AddMessage(xmlMsg, field);
			loadXml(ele, newMsg);
			ele = ele->NextSiblingElement();
		}							  											 
	}
		break;
	default:
		parseRepeatedString(field, rel, xmlMsg, attriNode->Value());
		break;
	}
	return true;
}

void xmlProto::parseRepeatedString(const FieldDescriptor* field, const Reflection* rel, Message *xmlMsg, const std::string &content)
{
	std::vector<std::string> strVec;
	this->splitStr(content, strVec);
	for (auto val : strVec)
	{
		switch (field->cpp_type())
		{
		case FieldDescriptor::CppType::CPPTYPE_INT32:
			rel->AddInt32(xmlMsg, field, atoi(val.c_str()));
			break;
		case FieldDescriptor::CppType::CPPTYPE_INT64:
			rel->AddInt64(xmlMsg, field, atoll(val.c_str()));
			break;
		case FieldDescriptor::CppType::CPPTYPE_UINT32:
			rel->AddUInt32(xmlMsg, field, atol(val.c_str()));
			break;
		case FieldDescriptor::CppType::CPPTYPE_UINT64:
			rel->AddUInt64(xmlMsg, field, atoll(val.c_str()));
			break;
		case FieldDescriptor::CppType::CPPTYPE_DOUBLE:
			rel->AddDouble(xmlMsg, field, atof(val.c_str()));
			break;
		case FieldDescriptor::CppType::CPPTYPE_FLOAT:
			rel->AddFloat(xmlMsg, field, atof(val.c_str()));
			break;
		case FieldDescriptor::CppType::CPPTYPE_BOOL:
			rel->AddBool(xmlMsg, field, (bool)atoi(val.c_str()));
			break;
		case FieldDescriptor::CppType::CPPTYPE_ENUM:
			//---
			break;
		case FieldDescriptor::CppType::CPPTYPE_STRING:
			rel->AddString(xmlMsg, field, val);
			break;
		default:
			break;
		}
	}
}

//---
std::string xmlProto::getFileData(const string &fileName)
{
	std::string data;
	char path[124] = { 0 };
	_getcwd(path, 124);
	std::string fullPath = path + string("\\") + fileName;
	auto fp = fopen(fullPath.c_str(), "r");
	if (fp)
	{
		fseek(fp, 0, SEEK_END);
		int len = ftell(fp);
		if (len)
		{
			fseek(fp, 0, SEEK_SET);
			char *buff = (char *)(malloc(len + 1));
			memset(buff, '\0', len + 1);
			fread(buff, 1, len, fp);
			fclose(fp);
			data = buff;
			free(buff);
		}
	}
	return data;
}

void xmlProto::splitStr(const std::string &content, std::vector<std::string> &strVec)
{
	strVec.clear();
	char *src = (char *)malloc(content.size() + 1);
	memcpy(src, content.c_str(), content.size() + 1);
	auto cc = strtok(src, ",");
	while (cc)
	{
		strVec.push_back(cc);
		cc = strtok(nullptr, ",");
	}
	free(src);
}

测试xml自动解析打印信息:

项目源码地址

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值