Golang: 解析JSON数据之一

JSON 作为目前最流行的数据传输格式, 相信每个程序员都跟它打过交道吧。使用 Go 语言时,也不可避免的要操作 JSON 数据,令人惊喜的是,Go 内置了序列化和反序列化 JSON 的功能,今天就来总结一下。

序列化是将结构对象转为 JSON 字符串,反序列化是将 JSON 字符串转为结构对象,它们分别对应 encoding/json 包下面的两个方法:

// 序列化 接收interface{}参数 返回字节切片
func Marshal(v interface{}) ([]byte, error) { }

// 反序列化 接收字节切片和interface{}参数 将结果反映在interface{}结构上
func Unmarshal(data []byte, v interface{}) error { }

marshal 这个单词的含义是 整理、编排、排列,对应的操作是将结构对象编排成 JSON 字符串,反之,unmarshal 是它的逆操作。

我们通过一个例子来演示这两个方法。假如我们有如下 data.json 文件:

{
  "group": "programmer",
  "persons": [
    {
      "name": "Jack",
      "age": 25
    },
    {
      "name": "Lily",
      "age": 20
    }
  ]
}

接下来,我们要读取这个文件,将 JSON 内容转为结构对象,然后更改对象数据:

// 读取JSON文件 将内容转为结构对象 然后更改数据

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
)

type (
    person struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }

    result struct {
        Group string `json:"group"`
        Persons []person `json:"persons"`
    }
)

func main() {
    var data result

    // 读取JSON文件内容 返回字节切片
    bytes, _ := ioutil.ReadFile("data.json")

    fmt.Println("*** data.json content: ***")

    // 打印时需要转为字符串
    fmt.Println(string(bytes))

    // 将字节切片映射到指定结构上
    json.Unmarshal(bytes, &data)

    fmt.Println("*** unmarshal result: ***")

    // 打印对象结构
    fmt.Println(data)

    // 更改数据
    data.Group = "engineer"

    // 将更改后的结构对象序列化成JSON格式
    newBytes, _ := json.Marshal(&data)

    fmt.Println("*** update content: ***")

    // 打印JSON结果
    fmt.Println(string(newBytes))
}

上面代码中,结构体字段的后面都有一串说明性信息,它们被称为标签(Tag),用于将结构体和 JSON 数据映射起来,如果不指定,系统会尝试以大小写无关的方式去匹配,但为了便于阅读和避免不必要的匹配过程,我们这里手动指定了具体的字段。

我们运行该程序,控制台会打印如下信息:

{
  "group": "programmer",
  "persons": [
    {
      "name": "Jack",
      "age": 25
    },
    {
      "name": "Lily",
      "age": 20
    }
  ]
}
*** unmarshal result: ***
{programmer [{Jack 25} {Lily 20}]}
*** update content: ***
{"group":"engineer","persons":[{"name":"Jack","age":25},{"name":"Lily","age":20}]}

最后的 JSON 数据还可以在格式化一下,我们可以利用下面这个方法:

// 带格式化的反序列化方法
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { }

相比 Marshal() 方法,MarshalIndent() 多了两个参数,分别是前缀和缩进,都是字符串类型。前缀一般不怎么常用,缩进可指定若干个空格,下面我们来改造一下:

// 将更改后的结构对象序列化成JSON格式
newBytes, _ := json.MarshalIndent(&data, "", "  ")

fmt.Println("*** indent content: ***")

// 打印JSON结果
fmt.Println(string(newBytes))

再次运行程序,打印结果如下:

*** indent content: ***
{
  "group": "engineer",
  "persons": [
    {
      "name": "Jack",
      "age": 25
    },
    {
      "name": "Lily",
      "age": 20
    }
  ]
}

最后,如果希望将结果写回到配置文件中的话,可以添加下面这一行代码:

ioutil.WriteFile("data.json", newBytes, os.ModeAppend)

WriteFile() 方法需要三个参数:文件名、字节切片数据、指定的文件操作权限。如果文件存在,这个方法先会清空文件内容,然后再写入新数据,如果文件不存在,则根据指定的第三个参数,去先创建指定的文件。

执行完上面这行代码,再去查看之前的 data.json 文件,就会发现,配置内容已经更新了。

posted @ 2019-06-03 08:03  liuhe688  阅读(7665)  评论(0编辑  收藏  举报