代码自动生成工具(三)-Xml读表代码自动生成工具

之前提到了一个工具类XmlReader


这里我们实现一个可以对Xml文件,自动生成其对应的读表代码。

本工具需要boost库支持,本人用的是1.55.0。


首先人为约定两个xml结点属性名称的保留关键字:pk=“”,rk=“”。

pk的值是本结点的某一个字段的名字,用来表示本结点在其父结点中,采用map来管理,键值就是该字段的值。

rk的值是本结点中某两个同类型的字段的名字,这两个字段用来表示一个闭区间,使得本结点在其父结点中,采用rangmap来管理,区间的起始值和终止值,就是这两个字段所填的值。

没有配置键的情况下,子节点在父结点中采用vector管理。

pk或rk 在相同结构路径下,只需要配置一次就可以,不需要每个节点都配置。


对于字段的数据类型,采用正则表达式判断识别,如果同一个字段,即出现整数,又出现浮点数,则按浮点数处理;即出现数字,又出现字符串,按字符串处理;如果同一个字段,出现不能兼容的两种类型,比如同时有数字和时间,按字符串处理。


这里首先定义Xml中支持的几种列类型:XmlMemberType:包括整数,小数,浮点数,字符串,时间戳,子结构等等

enum XmlMemberType
{
	XMT_init = 0,	// 未初始化的
	XMT_uint32,		// 无符号整数
	XMT_int32,		// 整数
	XMT_float,		// 浮点数
	XMT_datetime,	// 年-月-日 时:分:秒表示的时间
	XMT_time,		// 时:分:秒表示的时间
	XMT_rang,		// int-int这样的两个数字表示的区间值
	XMT_string,		// 字符串
	XMT_child,		// 子结构
	XMT_unknow,		// 非法的类型(同一个字段出现了两种不能互相兼容的类型)
	XMT_count,
};

列主键的类型

enum XmlMemberKey
{
	XMK_pk = 0,		// 主键
	XMK_rang,		// 范围主键
	XMK_normal,		// 普通成员
};

xml结构成员的定义,包括名字,类型,是否是主键

class XmlMember
{
public:
	XmlMember();
	XmlMember(const XmlMember &other);
	XmlMember& operator = (const XmlMember &other);
	void Init();
	std::string m_Name;		// xml结构字段名称
	XmlMemberType m_Type;	// xml结构字段类型
	XmlMemberKey m_Key;		// xml结构字段是否作为主键使用
};

xml结构的定义,包含结构名称,结构成员列表

class XmlStruct
{
public:
	XmlStruct();
	XmlStruct(const XmlStruct &other);
	XmlStruct& operator = (const XmlStruct &other);
	void Init();
	const XmlMember* GetPrimaryKey() const;
	std::vector<const XmlMember*> GetRangKey() const;
	std::string m_Name;							// 结构名称
	std::map<std::string, XmlMember> m_Members;	// 结构字段列表
};

整个xml文件:包括文件名,包名,结构列表

class XmlFile
{
public:
	XmlFile();
	XmlFile(const XmlFile &other);
	XmlFile& operator = (const XmlFile &other);
	void Init();
	std::string m_FileName;						// xml文件名(不带扩展名)
	std::string m_Package;						// xml包名xxx.xxx.xxx
	std::map<std::string, XmlStruct> m_Structs;	// xml结构列表
};

xml文件解析类,读取文件文本内容,使用EBNF范式,分析文件内容,按相应配置(命名空间,文件路径等),解析生成相应的XmlFile对象,

class XmlParse
{
public:
	XmlParse();
	~XmlParse();
	bool ParseFile(const char* fullPathName);
private:
	bool ParseXml(const char* expr);
	void ParseXmlStruct(const char* start, const char* end);
	void ParseXmlStructEnd(const char* start, const char* end);
	void ParseXmlStructEnd1(const char* start, const char* end);
	void ParseXmlStructEnd2(const char* start, const char* end);
	void ParseXmlMemberName(const char* start, const char* end);
	void ParseXmlMemberValue(const char* start, const char* end);
public:
	// 解析完毕的xml文件信息列表
	std::map<std::string, XmlFile> m_Files;
private:
	// 解析时,临时xml文件内容对象
	common::tool::FileData m_FileData;
	// 解析时,临时xml文件对象
	XmlFile m_TempFile;
	// 解析时,临时xml结构对象
	XmlStruct m_TempStruct;
	// 解析时,临时xml结构成员对象
	XmlMember m_TempMember;
	// 用于判断结构父子关系的栈 
	std::stack<std::string> m_ChildName;
	// xml文件解析工具输入参数
public:
	std::string m_NameSpace; // 自定义命名空间
	bool m_ThreadSpecific;	// 是否需要线程本地表格数据
	std::vector<std::string> m_XmlFileName; // 待处理的xml文件列表
	std::string m_LoaderPath; // loader输出cpp代码路径
	std::string m_HelperPath; // helper输出cpp代码路径
};


具体的代码生成包含两部分功能:

void ParseXml2LoaderH(const XmlParse& xmlParse)

void ParseXml2LoaderCpp(const XmlParse& xmlParse)

void ParseXml2HelperH(const XmlParse& xmlParse)

void ParseXml2HelperCpp(const XmlParse& xmlParse)

传入一个解析好的XmlParse对象,生成具体代码

Loader生成对应的读取代码,使用boost::ptree读取xml文件,转成相应的数据结构。

Helper生成一个空的辅助类,用于需要对数据重新组织的情况,比如配置中是按某个id列作为主键,存成一个map,提供查找,但是实际逻辑中还需要按某个名称列作为索引,再提供一个map,用于提供按名称查找(或者模糊查找),这时候就需要手动再提供一个新的查找函数,就可以写在helper中


main函数涉及两个小功能:

一个是遍历输入参数,解析名命名空间,待处理的xml文件,代码生成路径

一个是迭代每一个待处理的xml文件名,传入XmlParse解析,然后对解析完的结果,逐个生成相应代码


完整的代码,比较长,这里就不贴了


给一个生成后的代码的示例吧:

xml文件:

<Tests>
	<Node id="10000" pk="id">
		<Time begin="2018-1-18 00:00:00" end="2018-1-18 23:59:59" rk="begin;end" num="1"/>
		<Time begin="2018-1-19 00:00:00" end="2018-1-19 23:59:59" num="1"/>
		<Time begin="2018-1-20 00:00:00" end="2018-1-20 23:59:59" num="1"/>
	</Node>
	<Node id="10001">
		<Time begin="2018-1-18 00:00:00" end="2018-1-18 23:59:59" num="1"/>
		<Time begin="2018-1-19 00:00:00" end="2018-1-19 23:59:59" num="1"/>
		<Time begin="2018-1-20 00:00:00" end="2018-1-20 23:59:59" num="1"/>
	</Node>
</Tasks>
生成的代码:

#ifndef __TestsXmlLoader_h__
#define __TestsXmlLoader_h__

#include <string>
#include <vector>
#include <map>

#include "rangmap.h"

namespace common {
	namespace xml {

		class Tests
		{
		public:
			class Node
			{
			public:
				class Time
				{
				public:
					Time();
					Time(const Time& other);
					Time& operator=(const Time& other);
					~Time();

					time_t m_begin;
					time_t m_end;
					unsigned int m_num;
				};

			public:
				Node();
				Node(const Node& other);
				Node& operator=(const Node& other);
				~Node();

				void AddTime(time_t begin, time_t end, const Time& val);
				const Time* GetTime(time_t key) const;

				common::tool::rangmap<time_t, Time> m_Time;
				unsigned int m_id;
			};

		public:
			Tests();
			Tests(const Tests& other);
			Tests& operator=(const Tests& other);
			~Tests();

			void AddNode(unsigned int key, const Node& val);
			const Node* GetNode(unsigned int key) const;

			std::map<unsigned int, Node> m_Node;
		};

		class TestsXmlLoader
		{
		public:
			TestsXmlLoader()
			{
			}
			~TestsXmlLoader()
			{
			}

			bool LoadFile(const char* szPath);
			bool ReloadFile(const char* szPath);

			const Tests& GetTests() const;

		private:
			Tests m_Tests;
		};


	}
}
#endif

放上完整工程的 下载链接

工程中的boost库的路径为作者本机的路径,VS编译时请选择Release,重新配置boost库的include路径和lib路径

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值