vba xml 怎么设置父节点_熊二做了一个xml报文处理的开源库easyxml

自信、冷静、专注。—— TM 熊的自我勉励

850a56e94ba9a5be5989e24bb7f14c56.gif

1. 前言

熊二从去年开始,因项目需求接触到xml报文的处理,也是我第一次学习用C/C++的方式处理基于DOM模型的xml报文。因为本人比较懒hhh,所以第一反应就是去看看网上有没有什么大家都在用的比较好的xml处理开源库,站在前辈的肩膀上才能看的更远嘛。

2. 我找到了哪些库

库名称实现语言源码地址基于模型
Tinyxml2C++https://github.com/leethomason/tinyxml2DOM
libxml2Chttp://xmlsoft.org/sources/win32/libxml2-2.7.8.win32.zipDOM
CMarkupC++http://www.firstobject.com/Markup115.zipDOM
Mini-XMLChttp://www.msweet.org/files/project3/mxml-2.9.tar.gzDOM/SAX
Expat-XMLChttps://github.com/libexpat/libexpat/SAX
XercesC++/Java/Perlhttp://xerces.apache.org/mirrors.cgiDOM/SAX
  • SAX(Simple API for XML)是基于事件的,其基本工作流程是分析XML文档,当发现了一个新的元素时,产生一个对应事件,并调用相应的用户处理函数。这种方式占用内存少,速度快,但用户程序相应地会比较复杂。
  • DOM(Document Object Model)是在分析时,一次性地将整个XML文档进行分析,并在内存中形成对应的树结构,同时,向用户提供一系列的接口来访问和编辑该树结构。这种方式占用内存大,速度往往慢于SAX,但可以给用户提供一个面向对象的访问接口,对用户更为友好。

最后我选择了tinyxml2这个库作为项目使用,但关于这个库,网上的资料看了很多总觉得不算太理想。于是我尝试仅从使用者地角度去看看这个库有没有可能再封装一次,使其处理xml报文变得更简单?

3. 我用到了tinyxml库的哪些功能

  • 3.1. 根据 DOM模型,XML 文档中的每个成分都是一个节点。整个文档是一个文档节点,每一个xml元素是一个元素节点,而包含在xml元素中的文本是一个文本节点,每一个xml属性是一个属性节点,总之,基于DOM模型,xml文档皆为节点。tinyxml2正好利用了这一特性,以节点的概念来设计处理函数。
  • 3.2.  xml报文的处理在广义上来说,无非“增”、“删”、“改”、“查”四部分,我在处理xml报文时,用的最多的就是“增”、“查”,即协议交互中报文的组装和解析。通过将近半年的使用体验,不得不说,真的很感谢tinyxml2的作者Lee Thomason先生,能创作出这个好用的库并开源。接下来我会展示tinyxml组装和解析的步骤,并尝试优化我觉得不合理的地方。

4. xml报文的组装

比如我们要组装这样一段xml报文

<?xml version="1.0" encoding="UTF-8"?>
"south-bear">
0.0.0.0
8888
bear-2

这是用tinyxml2的基本接口组装报文的方法

XMLDocument xmlDoc;
XMLNode* parent = NULL;
XMLElement *child[16] = { NULL };

parent->InsertEndChild( xmlDoc.NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""));
child[1]=xmlDoc.NewElement("param"); //创建一个名为param的节点
child[1]->SetAttribute("name","south-bear"); //设置该节点属性
parent->InsertEndChild(child[1]); //将该节点加入到parent节点下

child[2]=xmlDoc.NewElement("ipAddress"); //创建一个名为ipAddress的节点
child[2]->SetText("0.0.0.0"); //设置该节点内容
child[1]->InsertEndChild(child[2]); //将该节点加入到param节点下

child[2]=xmlDoc.NewElement("portNo"); //创建一个名为portNo的节点
child[2]->SetText(8888); //设置该节点内容
child[1]->InsertEndChild(child[2]); //将该节点加入到param节点下

child[2]=xmlDoc.NewElement("channel"); //创建一个名为channel的节点
child[1]->InsertEndChild(child[2]); //将该节点加入到param节点下

child[3]=xmlDoc.NewElement("Name"); //创建一个名为Name的节点
child[3]->SetText("bear-2"); //设置该节点内容
child[2]->InsertEndChild(child[3]); //将该节点加入到channel节点下

怎么样?第一眼看上去是不是有点懵?感觉很不好记?如果是就对了,我当时第一反应也是这样。tinyxml2库在插入元素节点时都会用到NewElementInsertEndChild等接口,但是每插入一个节点后,想在该节点下插入下一级节点就很容易将节点搞混淆,就是说,你在使用tinyxml2库这个接口时,必须将每一个插入的节点记下,否者可能导致组装的xml报文就是混乱的,另外从篇幅上来看重复的代码还是占太多了。

于是我开始设想,有没有这样一种库,我只需要传入每一级节点的相关参数,就能自动组装xml报文,下面是我设想的组装xml的代码。

XMLDocument xmlDoc;
XMLNode* parent = NULL;
XMLElement *child[16] = { NULL };

child[1]=xmlAddNode(xmlDoc, parent, "param");//创建一个名为param的节点,将该节点加入到parent节点下
xmlSetNodeAttr(child[1], "name", "south-bear");//设置该节点属性

xmlAddNode(xmlDoc,child[1], "ipAddress", "0.0.0.0");//在param节点下添加ipAddress节点
xmlAddNode(xmlDoc,child[1], "portNo", 8888);//在param节点下添加portNo节点

child[2]=xmlAddNode(xmlDoc, child[1], "channel");//在param节点下添加channel节点
xmlAddNode(xmlDoc, child[2], "Name", "bear-2");//在channel节点下添加Name节点

xmlAddNodexmlSetNodeAttr接口是我在tinyxml2库的基础上封装的库。

对于xmlAddNode接口,你需要传入

  • xml文档对象
  • 想要创建的节点的父节点
  • 节点名称
  • 该节点的文本内容(可选)

对于xmlSetNodeAttr接口,你需要传入

  • 需要设置属性的节点
  • 属性名称
  • 属性内容 是不是容易理解多了,而且光看child数组元素的下标就能知道当前处在哪一级。操作起来也更方便

5. xml报文的解析

这是用tinyxml2的基本接口解析上述报文的方法

XMLDocument xmlDoc;
XMLElement *pXmlRoot = NULL;
XMLElement *pXmlTra = NULL;
char name[32] = {0};
char ipAddress[32] = {0};
int port = 0;

if(0 == xmlDoc.Parse((const char *)xmlBuf))
{
pXmlRoot = xmlDoc.RootElement();
pXmlTra = pXmlRoot->FirstChildElement("ipAddress");
if (pXmlTra != NULL)
{
strncpy(ipAddress, (pXmlTra->GetText()), (sizeof(ipAddress)-1));
}

pXmlTra = pXmlRoot->FirstChildElement("portNo");
if (pXmlTra != NULL)
{
sscanf(pXmlTra->GetText(), "%d", &port);
}

pXmlTra = pXmlRoot->FirstChildElement("channel");
pXmlTra = pXmlTra->FirstChildElement("Name");
if (pXmlTra != NULL)
{
strncpy(name, (pXmlTra->GetText()), (sizeof(name)-1));
}
}

emmmm....看上去还是很繁琐,能不能变得简洁点?下面是我设想的解析xml报文的代码,

XMLDocument xmlDoc;
XMLElement *pXmlRoot = NULL;
XMLElement *pXmlTra = NULL;
char name[32] = {0};
char ipAddress[32] = {0};
int port = 0;

if(0 == xmlDoc.Parse((const char *)xmlBuf))
{
pXmlRoot = xmlDoc.RootElement();

xmlGetNodeAttr(pXmlRoot, "name", name, sizeof(name));
xmlGetChildNode(pXmlRoot, "ipAddress", ipAddress, sizeof(ipAddress));
xmlGetChildNode(pXmlRoot, "portNo", &port);
xmlGetChildNode(pXmlRoot->FirstChildElement("channel"),"Name", channelName, sizeof(channelName));
}

xmlGetNodeAttrxmlGetChildNode接口是我在tinyxml2库的基础上封装的库。

对于xmlGetNodeAttr接口,你需要传入

  • 节点指针
  • 属性名称
  • 存放属性内容的缓冲区

对于xmlGetChildNode接口,你需要传入

  • 父节点
  • 想要获取的节点名称
  • 存放该节点内容的缓冲区

6.easyxml库

根据上面的设想,我基于tinyxml2库开发了easyxml库,这是源码目录树

├── demo
│   ├── create.cpp
│   ├── parser.cpp
│   └── test.xml
├── src
│   ├── easyxml.cpp
│   ├── easyxml.hpp
│   └── xml_cofig.h
└── third
├── tinyxml2.cpp
└── tinyxml2.h

目录树包含了依赖的tinyxml2源码和使用demo, 在src目录下的即为easyxml库的源码,easyxml库的接口有两个实现版本,用C++函数和宏来实现,两种版本各有优缺点,在使用中我们按照需求场景来选择。「C++函数接口」

  • 优点:利用c++函数的可重载性,解析各种类型的字段时,接口名称可以不变。
  • 缺点:因为是函数接口,存在实参转形参的过程。比如当获取字符串类型的字段时,为防止溢出,我们需要传入缓冲区的长度,这样一来函数的入参就要增加一个,观感上就会不整洁。

「宏接口」

  • 优点:因不存在存在实参转形参的过程,完美解决了C++函数接口的缺点,观感整洁。
  • 缺点:因宏不具备可重载性,所以解析各种类型的字段时,就要设计该种类型对应的宏接口

如上面目录所示,easyxml.hpp存放的是C++函数接口,xml_cofig.h存放的是宏接口 总结:如果是对于少量代码的工程,上面两种版本的选择没有太大的差别,但对于大量代码的工程,推荐选择宏接口。

「easyxml库的开源地址」https://github.com/southbear-club/easyxml

其中包含的接口当然不止上面示例代码中的这些库啦,当然一些更原子的操作还是要用tinyxnl2的基本接口的,不过easyxml库已经能帮你解决大部分的xml报文处理的需求了,这个库后续我会持续维护更新,以使得其能应用于更多的场景。欢迎star哟~

7. 关于tinyxml2

「传送门」如果在这之前,你还不太了解xml报文的基础知识,可以访问下面的链接恶补恶补。

https://www.runoob.com/dom/dom-intro.html

tinyxml2库的接口使用说明

http://leethomason.github.io/tinyxml2/annotated.html

「我遇到了哪些坑」

  • XMLDocument类中的Parse函数,可以选择传入"len"参数,即要解析的字节数。如果未指定,TinyXML-2将假定'xml'指向以NULL终止的字符串。
  • XMLPrinter类中的CStrSize()函数用以获取xml报文的总长度,但返回值长度包含终止符null,所以用CStrSize()函数获取xml的长度比实际值多1。
  • 如果你希望生成的xml报文是不带格式对齐的,那只需要在定义XMLPrinter类的对象时指定compact值为true即可,例如:

    XMLPrinter printer(0,true,0);

暂时就想到了这些坑,真是一把心酸泪,希望你们不要再去踩这些坑了。

8.唠唠叨叨

最近杭州突然变得好冷啊,大家记得添衣服别感冒呀,感冒了干啥都不开心。好啦,以上就是本期的全部内容,学习不是为了变得全知全能,而是为了不再害怕未知,我是熊二,我们下期再见。

推荐阅读:

从cmockery入门C语言单元测试

你的github开源项目还没做持续集成么?赶紧看看这个

还在为查内存泄漏问题痛苦不堪?试试这个神器

                                 1596355d0014ac271817ed0563d9841e.gif

                                    71334f7657a324c68aeda2c065b44e63.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/426983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux以oracle用户登录,Linux_oracle 10G for linux常用命令,首先以Oracle用户登录1、 - phpStudy...

oracle 10G for linux常用命令首先以Oracle用户登录1、 $/oracle/oracle/app/product/101/bin/dbstart&#xff1a;启动数据库。2、 $/oracle/oracle/app/product/101/bin/dbshut&#xff1a;关闭数据库。3、 $/oracle/oracle/app/product/101/bin/dbca&#xff1a;数据库配置助…

实例10:python

#题目&#xff1a;暂停一秒输出&#xff0c;并格式化当前时间。 #!/usr/bin/python -- coding: UTF-8 -- import time print (time.strftime(’%Y-%m-%d %H:%M:%S’,time.localtime(time.time()))) 暂停一秒 time.sleep(1) print (time.strftime(’%Y-%m-%d %H:%M:%S’,…

axios、ajax和xhr前端发送测试

其实axios和ajax都对原生的xhr进行了封装&#xff0c;个人感觉还是axios简洁、方便&#xff0c;尤大大都让我们转axios了&#xff0c;确实对于ajax好了不少&#xff0c;它支持了promise对象&#xff0c;支持js最新的规范。简单易用。 <!DOCTYPE html> <html lang"…

Python time strftime()方法

描述 Python time strftime() 函数接收以时间元组&#xff0c;并返回以可读字符串表示的当地时间&#xff0c;格式由参数format决定。 语法 strftime()方法语法&#xff1a; time.strftime(format[, t]) 参数 • format – 格式字符串。 • t – 可选的参数t是一个struct_time对…

iview 可以选择当天 禁用_人脸识别刚要普及,怎么就被禁用了?|人脸识别|人脸信息|世超|rekognition...

点击播放 GIF 0.5M人脸识别大家都不陌生了&#xff0c;从 iPhone 上面的 Face ID 再到支付宝刷脸支付&#xff0c;这个曾经只有在科幻电影上面才能见到的技术逐渐变得越来越司空见惯。点击播放 GIF 0.4M按理来讲&#xff0c;一项方便的技术应该越来越普及才对&#xff0c;不过世…

oracle ytd,hana ytd计算

YTD解答方案要求&#xff1a;输出字段: 年月, YTD金额因为我这边只有system一个账号&#xff0c;就用system账号写合同表的结构和数据如下:create column table "SYSTEM"."CONTRACTS"("CONTRACT_ID" INTEGER null, --合同ID"START_DATE&qu…

Python time mktime()方法

描述 Python time mktime() 函数执行与gmtime(), localtime()相反的操作&#xff0c;它接收struct_time对象作为参数&#xff0c;返回用秒数来表示时间的浮点数。 如果输入的值不是一个合法的时间&#xff0c;将触发 OverflowError 或 ValueError。 语法 mktime()方法语法&…

linux 识别文件类型,技术|Linux 中 7 个判断文件系统类型的方法

文件通过文件系统在磁盘及分区上命名、存储、检索以及更新&#xff0c;文件系统是在磁盘上组织文件的方式。文件系统分为两个部分&#xff1a;用户数据和元数据(文件名、创建时间、修改时间、大小以及目录层次结构中的位置等)。在本指南中&#xff0c;我们将用 7 种方法来识别你…

二分归并排序算法_第五篇排序算法|归并排序

0x01&#xff0c;前言闲叙最近几年很少看电视了&#xff0c;因为没时间看了&#xff0c;除了偶尔刷刷头条&#xff0c;基本上不会花大块的时间沉迷于电视剧&#xff0c;综艺&#xff0c;这或许就是短视频时代所带来的一些改变吧&#xff0c;我们都会深受其中。0x02&#xff0c;…

Python time asctime()方法

描述 Python time asctime() 函数接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"&#xff08;2008年12月11日 周二18时07分14秒&#xff09;的24个字符的字符串。 语法 asctime()方法语法&#xff1a; time.asctime([t])) 参数 • t – 9个元素的元组…

linux系统访问文件夹ls,Linux系统目录结构,文件类型以及ls、alias命令

一、Linux目录结构登录系统之后&#xff0c;在当前命令窗口输入以下指令&#xff0c;/是Linux操作系统里最核心的一个目录&#xff0c;所有的文件和目录全部在它下面&#xff0c;所以称为“根目录”&#xff1a;# ls / //列出根目录下的文件得到如图结果&#xff1a;如图上图所…

我所理解的闭包

闭包最主要概念&#xff1a;有权访问另一个函数作用域内变量的函数 function add(){var result;result function(x,y){return yx } return result }var sum new add(); console.log(sum(10,20))/*30*/ 函数会执行的主要原因就在于() function createFunct…

qq文件对方接收后一定会有提示吗_为什么微信、QQ不推出已读功能?因为它敢出网友就敢卸载...

12月14日&#xff0c;腾讯 QQ突然发起了一个"很吓人"的调查——你希望 QQ 出已读功能吗&#xff1f;在短时间内&#xff0c;有 超过3000 人参与&#xff0c;其中有 2660 人(71.6%)投票 "你出我就卸载 QQ"选项&#xff0c;大家旗帜鲜明地表示&#xff0c;并…

Python time localtime()方法

描述 Python time localtime() 函数类似gmtime()&#xff0c;作用是格式化时间戳为本地的时间。 如果sec参数未输入&#xff0c;则以当前时间为转换标准。 DST (Daylight Savings Time) flag (-1, 0 or 1) 是否是夏令时。 语法 localtime()方法语法&#xff1a; time.localt…

实例11:python

#古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c; #小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f; #!/usr/bin/python -- coding: UTF-8 -- #兔子的规律…

linux c c 常用的日志库,mslog: 一款超轻量级的C日志库,无需依赖额外的库,测试或移植过的系统有Linux(ubuntu,centos),Windows以及部分嵌入式设备;...

mslog1.综述​ 一款超轻量级的C日志库&#xff0c;无需依赖额外的库,库的设计目标为"简洁,可移植性强"。测试或移植过的系统有Linux(ubuntu,centos),Windows以及部分嵌入式设备。2.如何使用参考mslog_api_sample程序;关于编译与安装&#xff0c;可参见make help;3.功…

Vue中正确使用jQuery的方法

题主Vue小白&#xff0c;入门demo时想在其中使用jQuery&#xff08;当然可能是不推荐的做法哈&#xff0c;毕竟俩儿的风格不一样&#xff0c;但万一你就需要呢 _^ ^_&#xff09;&#xff0c;结果遇到问题&#xff0c;最终倒腾解决。 编译报错&#xff1a;$ is undefined or no…

python中superclass是什么_深度解析并实现python中的super(转载,好文)

大神半个月的成绩&#xff0c;让我看的叹为观止&#xff0c;建议看原帖地址&#xff0c;会让你对Python的描述符有更强的认识。原文链接&#xff1a;https://blog.csdn.net/zhangjg_blog/article/details/83033210深度解析并实现python中的super概述super的定义函数bound和描述…

实例12:python

#判断101-200之间有多少个素数&#xff0c;并输出所有素数。 #程序分析&#xff1a;判断素数的方法&#xff1a;用一个数分别去除2到sqrt(这个数)&#xff0c;如果能被整除&#xff0c;则表明此数不是素数&#xff0c;反之是素数。 #!/usr/bin/python -- coding: UTF-8 -- #…

linux显示gz文件字节大小,[原创] 如何不解压tar.gz文件查看其中的文件大小

如果你有一个上百G的tar.gz文件&#xff0c;要查看里面的每个文件的大小的话&#xff0c;解压出来可能太费时间&#xff0c;而且要占用大量磁盘空间。因此&#xff0c;不解压查看文件大小很有必要。不解压一个tar.gz文件&#xff0c;查看里面的文件明细的命令为&#xff1a;tar…