Go xml json 小结
最常见错误
struct 字段小写,序列化反序列化都无法访问——无法外部访问
解析 XML,反序列化,Unmarshal
-
如果 struct 的一个字段是 string 或者[]byt 类型且它的 tag 含有",inner xml “,Unmarshal 将会将此字段所对应的元素内所有内嵌的原始 xml 累加到此字段上 比如下边例子中的 description,最终输出为:
<server> <serverName>Shanghai_VPN</serverName> <serverIP>127.0.0.1</serverIP> </server> <server> <serverName>Beijing_VPN</serverName> <serverIP>127.0.0.2</serverIP> </server>
-
如果 struct 中有一个叫做 XMLName(必须是这个),且类型为 xml .Name 字段,那么在解析的时候就会保存这个 element 的名字到该字段,如下例子中的 servers 。
{ servers}
-
如果某个 struct 字段的 tag 定义中含有 XML 结构中 element 的名称,那么解析的时候就会把相应的 element 值赋值给该字段,如下 servername 和 serverip 定义。
-
如果某个 struct 字段的 tag 定义了中含有”,attr",那么解析的时候就会将该结构所对应的 element 的与字段同名的属性的值赋值给该字段,如下 version 定义。
-
如果某个 struct 字段的 tag 定义 型如"a>b>c",则解析的时候,会将 xml 结构 a 下面的 b 下面的 c 元素的值赋值给该字段。
-
如果某个 struct 字段的 tag 定义了"-",那么不会为该字段解析匹配任何 xml 数据。
-
如果 struct 字段后面的 tag 定义了",any",如果他的子元素在不满足其他的规则的时候就会匹配到这个字段。
-
如果某个 XML 元素包含一条或者多条注释,那么这些注释将被累加到第一个 tag 含有",comments"的字段上,这个字段的类型可能是[]byte 或 string,如果没有这样的字段存在,那么注释将会被抛弃。
只要设置对了 tag,那么 XML 解析就如上面示例般简单,tag 和 XML 的 element 是一一对应的关系, 如下所示,我们还可以通过 slice 来表示多个同级元素。
例子:
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
"os"
)
type Recurlyservers struct {
XMLName xml.Name `xml:"servers"`
Version string `xml:"version,attr"`
Svs []server `xml:"server"`
Description string `xml:",innerxml"`
}
type server struct {
//XMLName xml.Name `xml:"server"` // 不会有这个值
ServerName string `xml:"serverName"`
ServerIP string `xml:"serverIP"`
}
func main() {
file, err := os.Open("servers.xml") // For read access.
if err != nil {
fmt.Printf("error: %v", err)
return
}
defer file.Close()
data, err := ioutil.ReadAll(file)
if err != nil {
fmt.Printf("error: %v", err)
return
}
v := Recurlyservers{}
err = xml.Unmarshal(data, &v)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Println("===result====")
fmt.Println(v)
fmt.Println("===result====")
fmt.Println("===description====")
fmt.Println(v.Description)
fmt.Println("===description====")
}
// out
// ===result====
// {{ servers} 1 [{Shanghai_VPN 127.0.0.1} {Beijing_VPN 127.0.0.2}]
// <server>
// <serverName>Shanghai_VPN</serverName>
// <serverIP>127.0.0.1</serverIP>
// </server>
// <server>
// <serverName>Beijing_VPN</serverName>
// <serverIP>127.0.0.2</serverIP>
// </server>
// }
// ===result====
// ===description====
// <server>
// <serverName>Shanghai_VPN</serverName>
// <serverIP>127.0.0.1</serverIP>
// </server>
// <server>
// <serverName>Beijing_VPN</serverName>
// <serverIP>127.0.0.2</serverIP>
// </server>
// ===description====
官方例子:
type Email struct {
Where string `xml:"where,attr"`
Addr string
}
type Address struct {
City, State string
}
type Result struct {
XMLName xml.Name `xml:"Person"`
Name string `xml:"FullName"`
Phone string
Email []Email
Groups []string `xml:"Group>Value"`
Address
}
v := Result{Name: "none", Phone: "none"}
data := `
<Person>
<FullName>Grace R. Emlin</FullName>
<Company>Example Inc.</Company>
<Email where="home">
<Addr>gre@example.com</Addr>
</Email>
<Email where='work'>
<Addr>gre@work.com</Addr>
</Email>
<Group>
<Value>Friends</Value>
<Value>Squash</Value>
</Group>
<City>Hanga Roa</City>
<State>Easter Island</State>
</Person>
`
err := xml.Unmarshal([]byte(data), &v)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Printf("XMLName: %#v\n", v.XMLName)
fmt.Printf("Name: %q\n", v.Name)
fmt.Printf("Phone: %q\n", v.Phone)
fmt.Printf("Email: %v\n", v.Email)
fmt.Printf("Groups: %v\n", v.Groups)
fmt.Printf("Address: %v\n", v.Address)
输出:
XMLName: xml.Name{Space:"", Local:"Person"}
Name: "Grace R. Emlin"
Phone: "none"
Email: [{home gre@example.com} {work gre@work.com}]
Groups: [Friends Squash]
Address: {Hanga Roa Easter Island}
生成 xml,序列化,Marshal
func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) // 带缩进,前缀
我们看到 Marshal 函数接收的参数 v 是 interface{} 类型的,即它可以接受任意类型的参数,那么 xml 包,根据什么规则来生成相应的 XML 文件呢?
- 如果 v 是 array 或者 slice,那么输出每一个元素,类似 value
- 如果 v 是指针,那么会 Marshal 指针指向的内容,如果指针为空,什么都不输出
- 如果 v 是 interface,那么就处理 interface 所包含的数据
- 如果 v 是其他数据类型,就会输出这个数据类型所拥有的字段信息
生成的 XML 文件中的 element 的名字又是根据什么决定的呢?元素名按照如下优先级从 struct 中获取:
- 如果 v 是 struct,XMLName 的 tag 中定义的名称
- 类型为 xml.Name 的名叫 XMLName 的字段的值
- 通过 strcut 中字段的 tag 来获取
- 通过 strcut 的字段名用来获取
- marshal 的类型名称
我们应如何设置 struct 中字段的 tag 信息以控制最终 xml 文件的生成呢?
- XMLName 不会被输出
- tag 中含有"-“的字段不会输出
- tag 中含有"name,attr”,会以 name 作为属性名,字段值作为值输出为这个 XML 元素的属性,如上 version 字段所描述
- tag 中含有",attr",会以这个 struct 的字段名作为属性名输出为 XML 元素的属性,类似上一条,只是这个 name 默认是字段名了。
- tag 中含有",chardata",输出为 xml 的 character data 而非 element。
- tag 中含有",innerxml",将会被原样输出,而不会进行常规的编码过程
- tag 中含有",comment",将被当作 xml 注释来输出,而不会进行常规的编码过程,字段值中不能含有"–“字符串
- tag 中含有"omitempty”,如果该字段的值为空值那么该字段就不会被输出到 xml ,空值包括:false、0、nil 指针或 nil 接口,任何长度为 0 的 array, slice, map 或者 string
- tag 中含有"a>b>c",那么就会循环输出三个元素 a 包含 b,b 包含 c,例如如下代码就会输出
# Input
FirstName string `xml:"name>first"`
LastName string `xml:"name>last"`
#output
<name>
<first>Asta</first>
<last>Xie</last>
</name>
Json 概述
- JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于让人阅读。
- JSON 与 XML 最大的不同在于 XML 是一个完整的标记语言,而 JSON 不是。
- JSON 由于比 XML 更小、更快,更易解析,以及浏览器的内建快速解析支持,使得其更适用于网络数据传输领域。
解析 json
解析到结构体
解析到 interface
Go 类型和 JSON 类型的对应关系如下:
- bool 代表 JSON booleans,
- float64 代表 JSON numbers,
- string 代表 JSON strings,
- nil 代表 JSON null.
解析到 map[string]interface
键必须是 string
interface 到指定类型
通过断言之后,你就可以通过如下方式来访问里面的数据了
for k, v := range m {
switch vv := v.(type) {
case string:
fmt.Println(k, "is string", vv)
case int:
fmt.Println(k, "is int", vv)
case []interface{}:
fmt.Println(k, "is an array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is of a type I don't know how to handle")
}
}
生成 json,序列化,Marshal
针对 JSON 的输出,我们在定义 struct tag 的时候需要注意的几点是:
- 字段的 tag 是 “-",那么这个字段不会输出到 JSON
- tag 中带有自定义名称,那么这个自定义名称会出现在 JSON 的字段名中
- tag 中如果带有"omitempty"选项,那么如果该字段值为空,就不会输出到 JSON 串中
- 如果字段类型是 bool, string, int, int64 等,而 tag 中带有”,string"选项,那么这个字段在输出到 JSON 的时候会把该字段对应的值转换成 JSON 字符串
更好用的 github.com/bitly/go-simplejson
不用进行复杂的类型断言
package main
import (
"fmt"
"github.com/bitly/go-simplejson"
)
func main() {
js, err := simplejson.NewJson([]byte(`
{
"test": {
"array": [1, "2", 3],
"int": 10,
"float": 5.150,
"bignum": 9223372036854775807,
"string": "simplejson",
"bool": true
}
}`))
if err != nil {
panic(err)
}
arr, _ := js.Get("test").Get("array").Array()
i, _ := js.Get("test").Get("int").Int()
ms := js.Get("test").Get("string").MustString("test")
ms2 := js.Get("test").Get("string2").MustString("test")
fmt.Println("arr:", arr)
fmt.Println(i)
fmt.Println(ms)
fmt.Println(ms2)
}
参考
- 《go web 编程》
- 官方文档:https://studygolang.com/pkgdoc
- 原文作者:战神西红柿
- 原文链接:https://tomatoares.github.io/posts/go/Go-xml-json-%E5%B0%8F%E7%BB%93/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。