Dawn's Blogs

分享技术 记录成长

0%

Go Web编程学习笔记 (4) 文本处理-XML JSON 模板

Web 开发中需要对输入、输出进行处理。

XML 处理

示例 XML 文件如下:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<servers version="1">
<server>
<serverName>Shanghai_VPN</serverName>
<serverIP>127.0.0.1</serverIP>
</server>
<server>
<serverName>Beijing_VPN</serverName>
<serverIP>127.0.0.2</serverIP>
</server>
</servers>

读取 XML

读取 XML 选择 xml.Unmarshal 函数:

1
func Unmarshal(data []byte, v interface{}) error

将 XML 写入结构体为例,上述 XML 文件 对应的结构体为:

1
2
3
4
5
6
7
8
9
10
11
12
type Servers 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"`
}

读取操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func main() {
// 打开 XML 文件
file, err := os.Open("servers.xml")
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
}
// 将 XML 转为结构体
v := Servers{}
err = xml.Unmarshal(data, &v)
if err != nil {
fmt.Printf("error: %v", err)
return
}
fmt.Println(v)
}
// 输出:
// {{ servers} 1 [{{ server} Shanghai_VPN 127.0.0.1} {{ server} 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>
}

输出 XML

xml 包提供了两个函数 Marshal 和 MarshalIndent 来输出 XML,二者的区别在于 MarshalIndent 会增加前缀和缩进:

1
2
func Marshal(v interface{}) ([]byte, error)
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

构建结构体:

1
2
3
4
5
6
7
8
9
10
11
12
type Servers 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"`
}

解析结构体并输出 XML :

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
// 新建结构体
v := Servers{Version: "1"}
v.Svs = append(v.Svs, Server{ServerName: "Shanghai_VPN", ServerIP: "127.0.0.1"})
v.Svs = append(v.Svs, Server{ServerName: "Beijing_VPN", ServerIP: "127.0.0.2"})
// 转为 XML 数据
data, err := xml.MarshalIndent(&v, "", " ")
if err != nil {
fmt.Printf("error: %v\n", err)
}
os.Stdout.Write([]byte(xml.Header))
os.Stdout.Write(data)
}

注意:

之所以会有 os.Stdout.Write([]byte(xml.Header)) 这句代码的出现,是因为 xml.MarshalIndent 或者 xml.Marshal 输出的信息都是不带 XML 头的,为了生成正确的 xml 文件,我们使用了 xml 包预定义的 Header 变量。

JSON 处理

示例 JSON 格式数据如下:

1
2
3
4
5
6
7
8
9
10
11
12
{
"servers": [
{
"serverName": "Shanghai_VPN",
"serverIP": "127.0.0.1"
},
{
"serverName": "Beijing_VPN",
"serverIP": "127.0.0.2"
}
]
}

解析 JSON

json 包中有如下函数可以解析 JSON 数据:

1
func Unmarshal(data []byte, v interface{}) error

解析到结构体

上述 JSON 数据对应的结构体如下:

1
2
3
4
5
6
7
8
type Server struct {
ServerName string `json:"serverName"`
ServerIP string `json:"serverIP"`
}

type ServerSlice struct {
Servers []Server `json:"servers"`
}

解析 JSON 数据到结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
// 打开文件
file, err := os.Open("servers.json")
if err != nil {
fmt.Errorf("open file err: %v", err)
return
}
// 读取内容
data, _ := ioutil.ReadAll(file)
// 解析 JSON
v := ServerSlice{}
err = json.Unmarshal(data, &v)
if err != nil {
fmt.Errorf("open file err: %v", err)
return
}
fmt.Println(v)
}

解析到 interface

如果知道 JSON 数据的格式,可以解析到结构体中。如果不知道 JSON 数据格式,可以利用 map [string] interface {}[] interface {} 结构来存储任意的 JSON 对象。

1
2
var f interface{}
err := json.Unmarshal(b, &f)

此时,空接口 f 实际上是一个 map[string] interface{},其结构如下:

1
2
3
4
5
6
7
8
9
10
11
map[string]interface {}{
"servers":[]interface {}{
map[string]interface {}{
"serverIP":"127.0.0.1",
"serverName":"Shanghai_VPN"},
map[string]interface {}{
"serverIP":"127.0.0.2",
"serverName":"Beijing_VPN"
}
}
}

可以通过类型断言将**空接口转为 map[string] interface{}**,接着就可以利用 for range 对 map 进行遍历了:

1
m := f.(map[string]interface{})

目前,simplejson 包可以更加容易的处理未知结构的 JSON 数据,其 github 地址如下:

github.com/bitly/go-simplejson


生成 JSON

json 包提供了生成 JSON 数据的函数:

1
func Marshal(v interface{}) ([]byte, error)

这里不再举例说明具体操作。

模板处理

使用模板

解析模板

html/template 包中有两个函数可以解析模板:

  • ParseFiles 函数创建一个模板并解析filenames指定的文件里的模板定义。返回的模板的名字是第一个文件的文件名(不含扩展名),内容为解析后的第一个文件的内容。
1
func ParseFiles(filenames ...string) (*Template, error)
  • ParseGlob 函数创建一个模板并解析匹配 pattern 的文件里的模板定义。返回的模板的名字是第一个匹配的文件的文件名(不含扩展名),内容为解析后的第一个文件的内容。
1
func ParseGlob(pattern string) (*Template, error)

模板执行

html/template 包中有两个函数可以执行模板:

  • Execute 方法将解析好的模板应用到 data 上,并将输出写入 wr。
1
func (t *Template) Execute(wr io.Writer, data interface{}) error
  • ExecuteTemplate 方法类似 Execute,但是使用名为 nam e的 t 关联的模板产生输出。
1
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error

模板中插入数据

  • 字段操作{{.}} 代表当前对象,可以通过 {{.FieldName}} 访问当前对象的字段

  • 输出嵌套字段

    • {{with …}}…{{end}} 可以指定当前对象的值,如子结构体。
    • {{range …}}{{end}} 可以循环操作数据。
  • 条件判断{{if …}}…{{else if …}}…{{else}}…{{end}} 语句可以进行条件判断。

  • pipelines:在 {{}} 中的都是 pipeline,如 {{. | html}} 可以将当前对象进行 HTML 转义,变为 HTML 实体。

  • 模板变量:可以通过 $variable := pipeline 方式声明模板局部变量。

  • 模板嵌套

    • 声明:{{define "子模板名称"}}内容{{end}}
    • 调用:{{template "子模板名称"}}
  • 模板函数

    • 每一个模板函数都有一个唯一的名字,可以与一个 Go 函数相关联。FuncMap 类型定义了函数名字符串到函数的映射,每个函数都必须有 1 到 2 个返回值,如果有 2 个则后一个必须是 error 接口类型;如果有 2 个返回值的方法返回的 error 非 nil ,模板执行会中断并返回给调用者该错误。:
    1
    type FuncMap map[string]interface{}
    • 使用 t.Funcs 函数在模板中注册函数:
    1
    t = t.Funcs(template.FuncMap{"TmplFuncName": FuncName})
    • 在模板包内部已经有内置的实现函数:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var builtins = FuncMap{
    "and": and,
    "call": call,
    "html": HTMLEscaper,
    "index": index,
    "js": JSEscaper,
    "len": length,
    "not": not,
    "or": or,
    "print": fmt.Sprint,
    "printf": fmt.Sprintf,
    "println": fmt.Sprintln,
    "urlquery": URLQueryEscaper,
    }

Must 操作

Must 函数用来检查模板是否正确:

1
func Must(t *Template, err error) *Template

一般用于变量初始化:

1
var t = template.Must(template.New("name").Parse("html"))