使用微信支付的时候遇到这样一种情况:支付成功之后微信会发送一个通知过来,这个通知包含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) {
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
}
StartElement
和EndElement
类型都有这个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(使用前将#替换为@)