JSON 处理篇(下):未知结构 JSON 数据解码和 JSON 流式读写实现
解码未知结构的 JSON 数据
上篇教程学院君给大家介绍了 Go 语言内置的encoding/json 标准库以及如何通过它提供的方法对数据进行编解码。不过在上篇教程的示例中,要解码的 JSON 数据结构是已知的,在实际开发过程中,有时候我们可能并不知道要解码的 JSON 数据结构是什么样子的,这个时候,应该怎么处理呢?
类型转换
在前面介绍接口的时候,我们提到基于 Go 语言的面向对象特性,可以通过空接口来表示任何类型,所以同样,这也适用于未知结构的 JSON 数据解码:只需要将这段 JSON 数据解码输出到一个空接口即可。
在实际解码过程中,JSON 结构里边的数据元素将做如下类型转换:
- 布尔值将会转换为 Go 语言的
bool
类型; - 数值会被转换为 Go 语言的
float64
类型; - 字符串转换后还是
string
类型; - JSON 数组会转换为
[]interface{}
类型; - JSON 对象会转换为
map[string]interface{}
类型; -
null
值会转换为nil
。
在 Go 语言标准库 encoding/json
中,允许使用 map[string]interface{}
和 []interface{}
类型的值来分别存放未知结构的 JSON 对象或数组。
实例演示
下面我们以上篇教程的解码示例代码来做演示,只是这次,我们将解码结果映射到空接口对象:
u3 := []byte(`{"name": "学院君", "website": "https://laravel.geekai.co", "age": 18, "male": true, "skills": ["Golang", "PHP"]}`)
var user4 interface{}
err = json.Unmarshal(u3, &user4)
if err != nil {
fmt.Printf("JSON 解码失败:%v\n", err)
return
}
fmt.Printf("JSON 解码结果: %#v\n", user4)
在上述代码中,user4
被定义为一个空接口。json.Unmarshal()
函数将一个 JSON 对象 u3
解码到空接口 user4
中,最终 user4
将会是一个键值对的 map[string]interface{}
结构:
map[string]interface {}{"age":18, "male":true, "name":"学院君", "skills":[]interface {}{"Golang", "PHP"}, "website":"https://laravel.geekai.co"}
因为 u3
整体上是一个 JSON 对象,内部属性也会遵循上述类型转化规则一一转换。
访问解码后数据
要访问解码后的数据结构,需要先判断目标结构是否为预期的数据类型,然后,我们可以通过 for
循环搭配 range
语句一一访问解码后的目标数据:
user5, ok := user4.(map[string]interface{})
if ok {
for k, v := range user5 {
switch v2 := v.(type) {
case string:
fmt.Println(k, "is string", v2)
case int:
fmt.Println(k, "is int", v2)
case bool:
fmt.Println(k, "is bool", v2)
case []interface{}:
fmt.Println(k, "is an array:")
for i, iv := range v2 {
fmt.Println(i, iv)
}
default:
fmt.Println(k, "is another type not handle yet")
}
}
}
上述代码打印结果如下:
虽然有些烦琐,但的确是一种解码未知结构的 JSON 数据的安全方式。
JSON 的流式读写
此外 Go 语言内置的 encoding/json
包还提供了 Decoder
和 Encoder
两个类型,用于支持 JSON 数据的流式读写,并提供 NewDecoder()
和 NewEncoder()
两个函数用于具体实现:
func NewDecoder(r io.Reader) *Decoder
func NewEncoder(w io.Writer) *Encoder
下面我们通过实例来演示下 JSON 的流式读写,我们将从标准输入流中读取 JSON 数据,然后将其解码,最后再写入到标准输出流中:
# src/note/json/stream.go
package main
import (
"encoding/json"
"log"
"os"
)
func main() {
dec := json.NewDecoder(os.Stdin)
enc := json.NewEncoder(os.Stdout)
for {
var v map[string]interface{}
if err := dec.Decode(&v); err != nil {
log.Println(err)
return
}
if err := enc.Encode(&v); err != nil {
log.Println(err)
}
}
}
执行上述代码,我们需要先输入 JSON 结构数据供标准输入流 os.Stdin
读取,读取到数据后,会通过 json.NewDecoder
返回的解码器对其进行解码,最后再通过 json.NewEncoder
返回的编码器将数据编码后写入标准输出流 os.Stdout
并打印出来:
注:上述第一行是输入数据,第二行是输出数据
使用 Decoder
和 Encoder
对数据流进行处理可以应用得更为广泛些,比如读写 HTTP 连接、 WebSocket 或文件等,前面介绍的 Go 语言标准库 net/rpc/jsonrpc 就是一个应用了 Decoder
和 Encoder
的实际例子:
// NewServerCodec returns a new rpc.ServerCodec using JSON-RPC on conn.
func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
return &serverCodec{
dec: json.NewDecoder(conn),
enc: json.NewEncoder(conn),
c: conn,
pending: make(map[uint64]*json.RawMessage),
}
}
2 Comments
可以这样理解吗?Newdecoder()获取解码器,解码器调用decode进行解密。 同理Newencoder()获取编码器,跟encode进行编码。类似php的json_decode与json_encode,只不过这里要先获取解码器跟编码器,然后在执行操作
是的 原理都是一样的 只是 PHP 简化了