golang XML解析

2023-05-16

使用微信支付的时候遇到这样一种情况:支付成功之后微信会发送一个通知过来,这个通知包含xml格式的数据,其中有一个字段是这样的:

coupon_id_ n , 代 金 券 或 立 减 优 惠 I D , n ,代金券或立减优惠ID, n,ID,n为下标,从0开始编号

也就是说我们收到的xml可能是 <coupon_id_1></coupon_id_1>也可能是<coupon_id_10></coupon_id_10>总之这个字段的名字是随着n的变化而变化的,这样的xml我们在使用golang解析的时候直接给结构体设置TAG接着使用xml.Unmarshal解析是行不通的,因为这个TAG是不确定的。这里只能挨个读取xml元素进行解析了。

<xml>
  <h:appid xmlns:h="http://www.w3school.com.cn/furniture"><![CDATA[wx2421b1c4370ec43b]]></h:appid>
  <attach name="yuanjize"><![CDATA[支付测试]]></attach>
  <bank_type>CFT</bank_type>
  <fee_type><![CDATA[CNY]]></fee_type>
  <is_subscribe><![CDATA[Y]]></is_subscribe>
  <mch_id><![CDATA[10000100]]></mch_id>
  <nonce_str><![CDATA[<hello>5d2b6c2a8db53831f7eda20af46e531c</hello>]]></nonce_str>
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
  <out_trade_no><![CDATA[1409811653]]></out_trade_no>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
  <sub_mch_id><![CDATA[10000100]]></sub_mch_id>
  <time_end><![CDATA[20140903131540]]></time_end>
  <total_fee>1</total_fee>
  <coupon_fee><![CDATA[10]]></coupon_fee>
  <coupon_count><![CDATA[1]]></coupon_count>
  <coupon_type><![CDATA[CASH]]></coupon_type>
  <coupon_id><![CDATA[10000]]></coupon_id>
  <coupon_fee_0><![CDATA[100]]></coupon_fee_0>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml>

func XmlDecode(data string) map[string]string{
	decoder := xml.NewDecoder(strings.NewReader(data))
	result  := make(map[string]string)
	key := ""
	for{
		token, err := decoder.Token() //读取一个标签或者文本内容
		 if err==io.EOF{
		 	fmt.Println("parse Finish")
			 return result
		}
		if err!=nil{
			fmt.Println("parse Fail:",err)
			return result
		}
		switch tp := token.(type) {  //读取的TOKEN可以是以下三种类型:StartElement起始标签,EndElement结束标签,CharData文本内容
		case xml.StartElement:
			se := xml.StartElement(tp) //强制类型转换
			if se.Name.Local!="xml"{
				key=se.Name.Local
			}
			if len(se.Attr)!=0{ //读取标签属性
 				fmt.Println("Attrs:",se.Attr)
			}
			fmt.Println("SE.NAME.SPACE:",se.Name.Space) //读取命名空间
			fmt.Println("SE.NAME.LOCAL:",se.Name.Local) //读取标签名称
           fmt.Println()
		case xml.EndElement:
			ee := xml.EndElement(tp)
			if ee.Name.Local == "xml"{
				return result
			}
			fmt.Println("EE.NAME.SPACE:",ee.Name.Space)
			fmt.Println("EE.NAME.LOCAL:",ee.Name.Local)
		case xml.CharData: //文本数据,注意一个结束标签和另一个起始标签之间可能有空格
			cd := xml.CharData(tp)
			data := strings.TrimSpace(string(cd))
			if len(data)!=0{
			  result[key] = data
			  fmt.Println(key,",",data)
			}
		}
	}
}

上面的代码用微信的返回数据作为例子,代码比较简单,流程上就是读取一个TOKEN,判断TOKEN的类型然后把数据填到map里面。下面在说Name这个结构体之前先来料及一下XML的命名空间。

命名空间是什么?看一下下面这个例子,

<table>
    <leg>4<leg>
<table>

<table>
    <tr>
         <td>15<td>
         <td>16<td>
    <tr>
<table>

可以看到第一个table标签表示的是一个桌子,第二个table标签表示的是一个表格,如果把这个两个标签一起返回给你你就不知道两个标签各自代表的什么意思了,所以我们可以给标签加一个命名空间(也可以叫标签前缀)来区分它们:

<a:table>
    <leg>4<leg>
</a:table>

<b:table>
    <tr>
         <td>15<td>
         <td>16<td>
    <tr>
</b:table>

这样当读取到标签名字是table的时候,可以通过读取前缀来进行区分。

下面来说Name这个结构体:

type Name struct {
	Space, Local string
}

StartElementEndElement类型都有这个Name字段,它有两个字段,Space字段代表的是命名空间,Local字段是这个标签的名称。

比如<a:table> </a:table> 那么Space就是a,Local就是table。

如果命名空间是这样声明的<a:table xmls:a="www.baidu.com"> </a:table>,那么Space就是 www.baidu.com,Local还是table

这里再普及一下CDATA:xml解析器是不会解析CDATA标签中的内容。比如当解析器解析到<![CDATA[<hello>CNY</hello>]]>的时候解析出来的是一个文本类型,文本内容是<hello>CNY</hello>。也就是说把<hello>CNY</hello>看成一个单个的文本,不会去再解析hello标签。

最后贴一下上面程序的运行结果:

SE.NAME.SPACE: 
SE.NAME.LOCAL: xml

Attrs: [{{xmlns h} http://www.w3school.com.cn/furniture}]
SE.NAME.SPACE: http://www.w3school.com.cn/furniture
SE.NAME.LOCAL: appid

Attrs: [{{ name} yuanjize}]
SE.NAME.SPACE: b
SE.NAME.LOCAL: attach

SE.NAME.SPACE: 
SE.NAME.LOCAL: bank_type

SE.NAME.SPACE: 
SE.NAME.LOCAL: fee_type

SE.NAME.SPACE: 
SE.NAME.LOCAL: is_subscribe

SE.NAME.SPACE: 
SE.NAME.LOCAL: mch_id

SE.NAME.SPACE: 
SE.NAME.LOCAL: nonce_str

SE.NAME.SPACE: 
SE.NAME.LOCAL: openid

SE.NAME.SPACE: 
SE.NAME.LOCAL: out_trade_no

SE.NAME.SPACE: 
SE.NAME.LOCAL: result_code

SE.NAME.SPACE: 
SE.NAME.LOCAL: return_code

SE.NAME.SPACE: 
SE.NAME.LOCAL: sign

SE.NAME.SPACE: 
SE.NAME.LOCAL: sub_mch_id

SE.NAME.SPACE: 
SE.NAME.LOCAL: time_end

SE.NAME.SPACE: 
SE.NAME.LOCAL: total_fee

SE.NAME.SPACE: 
SE.NAME.LOCAL: coupon_fee

SE.NAME.SPACE: 
SE.NAME.LOCAL: coupon_count

SE.NAME.SPACE: 
SE.NAME.LOCAL: coupon_type

SE.NAME.SPACE: 
SE.NAME.LOCAL: coupon_id

SE.NAME.SPACE: 
SE.NAME.LOCAL: coupon_fee_0

SE.NAME.SPACE: 
SE.NAME.LOCAL: trade_type

SE.NAME.SPACE: 
SE.NAME.LOCAL: transaction_id
parse Finish

参考资料:
XML命名空间:http://www.w3school.com.cn/xml/xml_namespaces.asp
XML CDATA:http://www.w3school.com.cn/xml/xml_cdata.asp
golang XML解析:https://my.oschina.net/solate/blog/724958

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

golang XML解析 的相关文章

随机推荐

  • WIN10_GTX1650_深度学习环境搭建

    这篇博客总结的非常好 xff0c 但安装过程中可能会碰到一些问题 在这记录 xff0c 分享一下解决方案 https blog csdn net weixin 45755980 article details 105397874 Tenso
  • Linux面试必备20个常用命令

    文章目录 第一章 什么是linux第二章 linux的基础命令1 pwd 命令2 ls 命令3 cd 命令4 man 命令5 grep 命令6 find 命令7 chmod 命令8 ps 命令9 kill 命令10 tail 命令11 ne
  • Python爬虫实战(一):翻页爬取数据存入SqlServer

    目录 前言爬取目标准备工作代码分析1 设置翻页2 获取代理ip3 发送请求4 获取详情页地址5 提取详情信息6 存入数据库7 循环实现翻页8 启动 前言 x1f525 x1f525 本文已收录于Python爬虫实战100例专栏 xff1a
  • 已解决error: subprocess-exited-with-error

    已解决 xff08 pip安装第三方模块lxml模块报错 xff09 Building wheels for collected packages lxml Building wheel for lxml setup py error er
  • 已解决此处缺少‘,‘, ‘]‘字符, 实际上是一个 ‘EOF‘

    已解决Python解析JSON xff0c 抛出此处缺少 39 39 39 字符 实际上是一个 39 EOF 异常的解决方法 xff0c 亲测有效 文章目录 报错问题报错原因解决方法千人全栈VIP答疑群联系博主帮忙解决报错 报错问题 粉丝群
  • 已解决E: Unable to locate package ros-kinetic-desktop-full

    已解决Ubuntu安装ros xff0c 抛出异常E Unable to locate package ros kinetic desktop full的正确解决方法 xff0c 亲测有效 xff0c 文末附上Ubuntu系统对应ros系统
  • 数组元素交叉排列的算法题(a1 a2 a3 .. an b1 b2 b3 .. bn -->a 1 b1, a2 b2, a3 b3, .. an bn ) 概论思想(perfect shuffle 算法)

    perfect shuffle 算法 今天又发现一个关于完美洗牌的算法 这个比较简单一些 xff0c 由 microsoft的Peiyush Jain提出 原论文 xff1a A Simple In Place Algorithm for
  • Linux操作系统之命令

    Linux操作系统指令有很多 xff0c 这里就先介绍一些最最基础的吧 首先就是将操作界面显示 xff1a Ctrl 43 alt 43 t 显示当前目录内容 xff1a ls ls l xff1a 将目录内容使用列表显示 ls a xff
  • [操作系统]学习操作系统的经典书籍

    http blog chinaunix net u1 43966 showart 396940 html 介绍了一些操作系统学习的经典书籍 xff0c 包括理论上的 具体操作系统的 Abraham Silberschatz的两本书 xff1
  • 原创:史上最全最通俗易懂的,索引最左前缀匹配原则(认真脸)

    索引最左前缀匹配原则 对于最左前缀匹配原则居然没有百度百科 xff0c 实在是让我感觉不可思议 最左前缀匹配原则 xff0c 用几句话来概述就是 xff1a 顾名思义 xff0c 就是最左优先 xff0c 在创建多列索引时 xff0c 要根
  • MATLAB从文件读取数据

    一 从filename文件读取数据 1 readtable函数 语法 xff1a t 61 readtable xff08 filename xff09 支持的扩展名 xff1a txt csv xls xlsm xlsx xlsm xlt
  • 前端进阶之TS总结

    知识点 高频面试题TS装饰器axios二次封装 1 高频面试题 1 1 类型推论 amp 可赋值性 什么是类型推论 xff1f TypeScript 会在没有明确的指定类型的时候推测出一个类型 xff0c 这就是类型推论如果定义的时候没有赋
  • 岁月清浅,邀你入梦

    这世间本应美好 xff0c 怎无奈痛苦缠身 xff0c 卿心亦真 xff0c 免世人之苦 xff0c 乐自身之本 卿之容 xff0c 多沉醉 xff0c 于心赞 xff0c 日夜思 淡若微风的陪伴 xff0c 奈何情深缘浅 只相识 xff0
  • 记一次解BUG的心得感受

    今天遇到 了 一个 STP的问题 xff0c 从测试 现象 来看与之前一个FR的验证过程中表现出来的特征很相似 这种相似性将我引入了一种歧途 xff1a 怀疑原来的修改有问题 假设你知道第N次修改有潜在的case无法验证 那么这种潜在的风险
  • 02_Keil5报错 error: #5: cannot open source input file “XXX.h”: No such file or directory解决方法

    Keil5 error 5 cannot open source input file led h No such file or directory 是找不到包含文件 解决办法1 包含文件可以解决 解决办法2 如果包含了还是报 5找不到文
  • 05_FreeRTOS中断管理

    目录 什么是中断 中断相关寄存器 源码实验 什么是中断 简介 让CPU打断正常运行的程序 转而去处理紧急的事件 程序 就叫中断 举例 上课可以比做CPU正常运行的程序 上厕所可以比做中断程序 中断执行机制 可简单概括为三步 中断请求 外设产
  • 07_FreeRTOS任务调度器的挂起和恢复

    任务调度器的挂起和恢复 挂起任务调度器 调用此函数不需要关闭中断 使用格式示例 1 与临界区不一样的是 挂起任务调度器 未关闭中断 2 它仅仅是防止 xff1b 任务之间的资源争夺 中断照样可以直接响应 3 挂起调度器的方式 适合于临界区位
  • 09_FreeRTOS任务调度器

    目录 开启任务调度器vTaskStartScheduler函数 xPortStartScheduler开启任务调度器函数 启动第一个任务 prvStartFirstTask开启第一个任务函数 vPortSVCHandler SVC中断服务函
  • 13_FreeRTOS消息队列

    目录 队列简介 FreeRTOS队列特点 队列操作基本过程 队列结构体介绍 队列结构体整体示意图 队列相关API函数介绍 创建队列相关API函数介绍 往队列写入消息API函数 往队列写入消息函数入口参数解析 从队列读取消息API函数 实验源
  • golang XML解析

    使用微信支付的时候遇到这样一种情况 xff1a 支付成功之后微信会发送一个通知过来 xff0c 这个通知包含xml格式的数据 xff0c 其中有一个字段是这样的 xff1a coupon id n 代 金 券