【XML协议】轻松掌握使用C++ XML解析库——pugixml

news2024/12/23 17:41:04

文章介绍了xml协议的组成以及C++ xml解析库pugixml的常用操作。源于开发中每次遇到xml操作时,都要回过头查看pugixml库常用操作时什么样的,能不能有个更深刻和清晰的认识呢?其实搞清楚xml结构和pugixml组织结构的对照关系,以及pugixml中节点、属性的增删改查逻辑,可以帮助我们快速回忆起这些东西。遂,本文留作查询使用。

XML协议

XML(Extensible Markup Language)是一种可扩展标记语言,在后端开发中,常作为一种数据格式进行消息的传输、存储。我们先来了解一下XML协议通常包含哪些内容。

  1. XML声明

<?xml version="1.0" encoding="UTF-8"?> xml声明常位于文件头,指明xml版本、编码、standalone。

  • version: 指明xml版本
  • encoding: 指明文本编码
  • standalone: “yes” 表示不需要依赖外部文件,"no"表示依赖外部文件,一般用于schema校验等(后端目前没碰到这种场景,不再深究)
  1. PI(processing instruction)

<?xml-stylesheet type="text/xsl" href="style.xsl"?> pi信息通常是指明样式表给到浏览器使用。

  1. 文档类型(doc type)

<!DOCTYPE library [
   <!ELEMENT library (book+)>
   <!ELEMENT book (title, author, description)>
   <!ELEMENT title (#PCDATA)>
   <!ELEMENT author (firstName, lastName)>
   <!ELEMENT firstName (#PCDATA)>
   <!ELEMENT lastName (#PCDATA)>
   <!ELEMENT description (#PCDATA)>
]> 

文档类型,对xml内容的解释,常用于浏览器校验xml内容是否合法。

  1. 文档、节点、属性、注释

xml正文是由一个个节点组成,每个节点可以包含若干子节点、属性。下图给出了一个xml示例及其组成,最右侧是对应到pugixml库中的节点类型。
xml结构示例

注意这里的data和cdata, 后者是原始文本不会被xml解析,例如有时我们的属性包含一些特殊字符,这些字符可能会破坏xml结构,此时要作用cdatta使用。

pugixml解析库

pugixml项目,是一个C++ xml解析库,主要实现包含在pugixml.h pugixml.cpp pugiconfig.hpp 三个文件中,提供了xml解析、生成、xpath等功能。主要包含以下三个类:
pugixml主要的类

  • xml_document: 对应着一个xml文档
  • xml_node: 对应着xml中的节点
  • xml_attribute: xml节点的属性

解析

pugixml解析提供从字符流、字符串、buffer、文件这几种方式加载,解析就是生成和初始化xml_document的过程。
加载方法
下面是一个示例,

  const std::string src = R"(
<Document user_id="5001">
  <display>
    <mode>1</mode>
    <item color="blue" name="global" />
    <item color="red" name="global" />
  </display>
</Document>
)";
  pugi::xml_document doc;
  // 从字符串加载,不能指定编码类型
  pugi::xml_parse_result result = doc.load_string(src.c_str(), pugi::parse_default);
  // 从buffer中加载,可以指定buffer中的编码类型
  result = doc.load_buffer(src.c_str(), src.size(), pugi::parse_default, pugi::encoding_utf8);
  // 从本地文件中加载
  result = doc.load_file("./test.xml", pugi::parse_default, pugi::encoding_utf8);
  if (result.status == pugi::xml_parse_status::status_ok) {
    doc.print(std::cout);
  } else {
    std::cout << result.description() << std::endl;
  }

load返回的是parse_result类型,该类型记录了加载的状态码、编码、加载偏移、描述信息等。

	struct PUGIXML_CLASS xml_parse_result {
		// Parsing status (see xml_parse_status)
		xml_parse_status status;
		// Last parsed offset (in char_t units from start of input data)
		ptrdiff_t offset;
		// Source document encoding
		xml_encoding encoding;
		// Default constructor, initializes object to failed state
		xml_parse_result();
		// Cast to bool operator
		operator bool() const;
		// Get error description
		const char* description() const;
	};

查询

xml_document 继承自xml_node, 除了父类方法外,自己实现了load,reset这些逻辑,其他接口和父类一致,所以增删改查也就是xml_node的增删改查。增删改是在查到指定节点后进行set操作,所以掌握查询方法更为关键。

<Document user_id="5001" >
  <display>
    <mode>1</mode>
    <item color="blue" name="global" />
    <item color="red" name="global" />
  </display>
</Document>
  • 根据节点名称获取节点
    例如查询mode节点相关的信息:
    pugi::xml_node node = doc.child("Document").child("display").child("mode");
    std::cout << "name=" << node.name() << ",text=" << node.text() << std::endl;
  • 根据xpath获取节点(select_node)
    这里需要明确,select返回的是xpath_node类型,该类型可能包含的是节点,也可能是属性,需要调用方清晰知道到底是哪一种类型以便调用不同的方法获取值。
  pugi::xpath_node xpath_result = doc.select_node("/Document/display/mode");
  std::cout << "name=" << xpath_result.node().name()
            << ",text=" << xpath_result.node().text() << std::endl;
  • 根据属性名获取属性(attribute("name"))
std::cout << doc.child("Document").attribute("user_id").as_string() << std::endl;
  • 根据xpath获取属性(/.../node/@key)
  pugi::xpath_node first_item =
      doc.select_node("/Document/display/item/@color");
  std::cout << "name=" << first_item.attribute().name()
            << ",text=" << first_item.attribute().value() << std::endl;
  • 获取指定属性的节点(/.../node[@attr='val'])
  auto ret = doc.select_node("/Document/display/item[@color='blue']");
  std::cout << "name=" << ret.node().name() << std::endl;
  • 遍历节点
    提供了循环遍历的迭代器,也可以使用for-each。遍历属性与此类似,不再赘述
  // 1. children
  {
    auto children = display.children();
    for (const auto &ele : children) {
      std::cout << ele.name() << std::endl;
    }
  }
  {
    auto children = display.children("item");
    for (const auto &ele : children) {
      std::cout << ele.name() << std::endl;
    }
  }

  // 2. itr
  for (auto itr = display.begin(); itr != display.end(); ++itr) {
    std::cout << itr->name() << std::endl;
  }

修改

对于一个节点来说,有哪些内容呢。节点本身的name, text, 以及包含的属性(属性名称、属性值)。
注意节点的text同样是一个结构体,不存在直接的set_text()方法
插入节点时提供了一些带位置参数的接口,例如在某节点之前和之后插入。

    pugi::xml_node ret =
        doc.select_node("/Document/display/item[@color='blue']").node();
    ret.set_name("item_new");
    ret.text().set("abs");
    // ret.set_text();
    // 注意这里,设置text值,text是一个结构,不存在直接的set_text()方法
    ret.set_value("abs");

    ret.attribute("color").set_value("new blue");     // 修改已有属性值
    ret.append_attribute("modify").set_value("2024"); // 添加新的属性

    auto new_node = ret.parent().append_child("item");  // 在其后追加节点
    new_node.append_attribute("color").set_value("white");
    doc.print(std::cout);

删除

  • xml_document 重置状态时reset接口。
  • remove_child、remove_attribute 对应移除单个子节点和属性;remove_children、remove_attributes对应移除所有子节点和属性。

序列化

将document生成字符串的过程

  • 字符流
    format参数可以自定义,这里取得是不进行格式化。
  std::stringstream out;
  doc.save(out, "\t", pugi::format_raw);
  std::cout << out.str() << std::endl;
  • 字符串
class XmlStringWriter : public pugi::xml_writer {
public:
  void write(const void *data, size_t size) {
    result.assign(reinterpret_cast<const char *>(data), size);
  }

  std::string result;
};

XmlStringWriter writer;
doc.save(writer);
std::cout << writer.result << std::endl;

总结

首先,使用pugixml时要注意,该库支持unicode编码,是不支持gbk编码的,解析时对于gbk类型要自己进行转码。
其次,pugixml提供了一些解析的选项,默认选项有时可能会出问题,例如value包含tab时,默认的反序列化参数可能会直接消除tab,关于这些选项(const unsigned int parser_x这些参数),仓库由比较明细的解释。同样,序列化时也有一些选项(const unsigned int format_xx)。
然后,要快速掌握该库的使用,需要明确xml包含哪些结构,例如版本头、节点、属性、注释等,以及对应到pugixml库是哪些结构。需要留意的是对于属性值、text值都是一个结构体,你要知道你操作的不是一个简单的string类型。
最后,该库提供了xpath功能,select_node和select_nodes 返回值可能是包含的节点也可能是属性,调用者要知道自己在筛选什么。而对于xpath至少要搞清楚如何查节点(/.../node)、查属性(/.../node/@attr)、查指定属性的节点((/.../node[@attr='val'])这几个xpath格式是什么样的。

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

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

相关文章

《无线重构世界》射频模组演进

射频前端四大金刚 射频前端由PA、LNA、滤波器、开关“四大金刚” 不同的模块有自己的工艺和性能特点 分层设计 射频前端虽然只由PA、LNA、开关、混频器4个模块构成&#xff0c;但不同模块之间相互连接且相互影响。如果将射频系统当成一个整体来理解&#xff0c;其中的细节和…

《Python编程实训快速上手》第五天--模式匹配与正则表达式

一、不用正则表达式查找文本模式 文本模式是一种人为规定的结构&#xff0c;现在有一个模式&#xff1a;3个数字-3个数字-4个数字 使用isPhoneNumber()函数来判断字符串是否匹配该模式 def isPhoneNumber(number):if len(number) ! 12:return Falsefor i in range(0,3):if n…

文件读写函数(1)

大家好&#xff0c;今天我们来介绍一下文件读写函数&#xff0c;昨天我们已经简单提及了一下文件的概念&#xff0c;那么我们今天就不多说&#xff0c;下面我们来看函数。 1.fopen函数 这个函数是用来打开文件的&#xff0c;它的两个参数分别是文件名和文件的打开模式&#x…

华为数通HCIA系列第5次考试-【2024-46周-周一】

文章目录 1、子网掩码有什么作用&#xff0c;和IP地址是什么关系&#xff0c;利用子网掩码可以获取哪些信息&#xff1f;2、已知一个IP地址是192.168.1.1&#xff0c;子网掩码是255.255.255.0&#xff0c;求其网络地址3、已知某主机的IP地址是192.168.100.200&#xff0c;子网掩…

Linux(CentOS)运行 jar 包

1、在本地终端运行&#xff0c;关闭终端&#xff0c;程序就会终止 java -jar tlias-0.0.1-SNAPSHOT.jar 发送请求&#xff0c;成功 关闭终端&#xff08;程序也会终止&#xff09; 发送请求&#xff0c;失败 2、在远程终端运行&#xff0c;关闭终端&#xff0c;程序就会终止 …

GIT:如何查找已删除的文件的历史记录

首先你得知道文件的名称和路径 然后打开 gitlab&#xff0c;到项目中&#xff0c;仓库-> 文件 查找文件 复制文件名到可能存在过这个文件的分支当中&#xff0c;就能看到了

C++builder中的人工智能(21):Barabási–Albert model(BA)模型

在此之前&#xff0c;大多数网络被想当然的认为是随机的&#xff0c;因此连接度分布可以近似用泊松分布来表示&#xff0c;而巴拉巴西与其学生阿尔伯特、郑浩雄通过对万维网度分布测量的结果却显示万维网度分布服从幂律分布&#xff0c;存在枢纽节点&#xff08;拥有大量链接的…

新手 Vue 项目运行

前言&#xff1a;前面讲了我们已经将spingboot项目运行起来了&#xff0c;现在我们只需将后台管理的Vue项目运行起来即可完成整个项目。 在运行vue项目之前&#xff0c;请先运行springboot项目&#xff0c;运行步骤请看&#xff1a;运行Springboot Vue 项目_springbootvue项目…

AUTOSAR_EXP_ARAComAPI的7章笔记(2)

☞返回总目录 相关总结&#xff1a;服务发现实现策略总结 7.2 服务发现的实现策略 如前面章节所述&#xff0c;ara::com 期望产品供应商实现服务发现的功能。服务发现功能基本上是在 API 级别通过 FindService、OfferService 和 StopOfferService 方法定义的&#xff0c;协议…

计算机网络分析题

网络的布置 根据具体需求布置网络 第二小题、网络的划分 根据路由表作出路由器拓扑图 ARP跨网络寻址 TCP报文段格式概念 网桥的转发表与动作 网络嗅探报文 十六进制化作十进制 嗅探以太网帧首部 除MAC帧以外&#xff0c;其他各层协议数据单元都是源地址在前&#xff0c;目…

【初阶数据结构与算法】线性表之链表的分类以及双链表的定义与实现

文章目录 一、链表的分类二、双链表的实现1.双链表结构的定义2.双链表的初始化和销毁初始化函数1初始化函数2销毁函数 3.双链表的打印以及节点的申请打印函数节点的申请 4.双链表的头插和尾插头插函数尾插函数 5.双链表的查找和判空查找函数判空函数 6.双链表的头删和尾删头删函…

【AI写作宝-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

【HarmonyNext】显示提示文字的方法

【HarmonyNext】显示提示文字的方法 本文介绍在 HarmonyNext 中显示提示文字的两种常见方法&#xff1a;使用自定义弹窗 CustomDialog 和使用 promptAction 的 showToast 方法。 一、使用自定义弹窗 CustomDialog 在 HarmonyNext 中&#xff0c;自定义弹窗是实现复杂提示信…

【3D Slicer】的小白入门使用指南

一、3D Slicer认识 3D Slicer是一个开源医学影像分析和可视化平台(本质是TotalSegmentator的软件版)。(补充:TotalSegmentator 是一个用于医学图像分割的开源工具,能够对104种解剖结构进行精确分割。该项目基于深度学习技术,支持CT和MR图像的处理。TotalSegmentator 提供…

ts定义接口返回写法

接口&#xff08;未进行ts定义&#xff09; export async function UserList(params: {// keyword?: string;current?: number;pageSize?: number;},// options?: { [key: string]: any }, ) {return request<API1.UserList>(http://geek.itheima.net/v1_0/mp/artic…

.NET Core 应用程序如何在 Linux 中创建 Systemd 服务 ?

.NET Core 和 Linux 已经成为一个强大的组合&#xff0c;为开发人员提供了一个灵活、高性能的平台来构建和运行应用程序。在 Linux 上部署 .NET Core 应用程序的一个关键方面是利用 systemd 服务来确保应用程序顺利运行&#xff0c;在开机时自动启动&#xff0c;并在失败后重新…

低代码平台总览

低代码平台&#xff08;Low-Code Platform&#xff09;是一种软件开发工具&#xff0c;它允许用户通过图形化界面和少量的编码来快速构建应用程序。低代码平台的核心理念是通过抽象和最小化手工编码的方式&#xff0c;加速软件开发和部署的过程。以下是低代码平台的一些关键特性…

分布式----Ceph部署(上)

目录 一、存储基础 1.1 单机存储设备 1.2 单机存储的问题 1.3 商业存储解决方案 1.4 分布式存储&#xff08;软件定义的存储 SDS&#xff09; 1.5 分布式存储的类型 二、Ceph 简介 三、Ceph 优势 四、Ceph 架构 五、Ceph 核心组件 #Pool中数据保存方式支持两种类型&…

华为诺亚方舟新作:GUI Agent综述

1、摘要 智能代理可以做更复杂的任务。特别是模拟人类在GUI上进行交互&#xff0c;比如点击和打字。 本论文对此进行总结&#xff0c;特别是其中最关键的数据、框架和应用。 首先是数据集和基准。 其次是统一框架&#xff0c;涵盖了关键组件和分类体系。 此外是基于MLLM的GUI…

(65)使用RLS自适应滤波器进行信道均衡的MATLAB仿真

文章目录 前言一、仿真说明二、码间串扰、色散、与频率选择性衰落1. 码间串扰&#xff08;ISI&#xff09;2. 信道的色散与码间串扰3. 减少ISI的方法 三、MATLAB仿真代码四、仿真结果1.发送16QAM信号的星座图2.信道的频率响应3.接收16QAM信号的星座图4.均衡后16QAM信号的星座图…