JSON(JavaScript Object Notation)是一种比XML更轻量级的数据交换格式,在易于人们阅读和编写的同时,也易于程序解析和生成。尽管JSON是JavaScript的一个子集,但JSON采用完全独立于编程语言的文本格式,且表现为key/value的文本描述形式(与GO语言中的map极为相似),这使它成为较理想的、跨平台的、跨语言的数据交换语言。记得初次接触JSON这种数据形式是在刚工作时,当时在js页面中处理非常方便,印象一直很深刻。后来,学习了一些mongodb,完全处理这种格式的数据。原来,个人很不喜欢像这种键-值对的数据形式,因为总觉得这个底层可能映射起来比较方便,不过,好多情况下确实是少不了这种形式。
Go语言内建对JSON的支持,使用GO语言内置的encoding/json标准库,开发者可以轻松使用GO程序生成和解析JSON格式的数据。
举个例子:
package main import ( "encoding/json" "fmt" ) type Book struct { Title string Author []string Publisher string Price float64 IsPublished bool } func main() { b := []byte(`{ "Title":"go programming language","Author":["john","ada","alice"],"Publisher":"qinghua","IsPublished":true,"Price":99 }`) //先创建一个目标类型的实例对象,用于存放解码后的值 var book Book err := json.Unmarshal(b,&book) if err != nil { fmt.Println("error in translating,",err.Error()) return } fmt.Println(book.Author) }
Json.Unmarshal()函数会根据一个约定的顺序查找目标结构中的字段,如果找到一个则进行匹配。这些字段在类型声明中必须都是以大写字母开头、可被导出的字段。
但是当JSON数据中的数据结构和GO中的目标类型不匹配时,会怎样呢?如果JSON中的字段在GO目标类型中不存在,json.Unmarshal()函数在解码过程中会丢弃该字段。这个特性让我们可以从一段JSON数据中筛选出指定的值填充到多个GO语言类型中,当然,前提是已知JSON数据的字段结构。这也意味着,目标类型中不可被导出的私有字段(非首字母大写)将不会受到解析转化的影响,
如果要解析一个配置文件,为了使程序端的改动能够灵活,大多数情况下是不知道配置文件中到底是怎样的结构,只是在需要的时候去取就可以了。GO语言支持接口。在go中,接口是一组预定义方法的组合,任何一个类型均可通过实现接口预定义的方法来实现,且无需显示声明,所以没有任何方法的空接口代表任何类型。换句话说,每一个类型其实都至少实现了一个空接口。GO内建这样灵活的类型系统,向我们传达了一个很有价值的信息:空接口是通用类型。如果要解析一段未知结构的JSON,只需向这段JSON数据解码输出到一个空接口即可。在GO的标准库encoding/json包中,允许使用map[string]interface{}和[]interface{}类型的值来分别存放未知结构的JSON对象或数组。
还是刚才的例子,只是将结构体去掉,变成一个未知结构的数据。
package main import ( "encoding/json" "fmt" ) func main() { b := []byte(`{ "Title":"go programming language","Price":99 }`) //先创建一个目标类型的实例对象,用于存放解码后的值 var inter interface{} err := json.Unmarshal(b,&inter) if err != nil { fmt.Println("error in translating,err.Error()) return } //要访问解码后的数据结构,需要先判断目标结构是否为预期的数据类型 book,ok := inter.(map[string]interface{}) //然后通过for循环一一访问解码后的目标数据 if ok { for k,v := range book { switch vt := v.(type) { case float64: fmt.Println(k," is float64 ",vt) case string: fmt.Println(k," is string ",vt) case []interface{}: fmt.Println(k," is an array:") for i,iv := range vt { fmt.Println(i,iv) } default: fmt.Println("illegle type") } } } }
说实话,这样转化确实很繁琐,但也是解析未知结构的JSON数据的方式。
原来试图使用XML来做配置文件,但是,那样的话必须要知道有哪些数据项(Key),而对于灵活的配置文件来说,到底包括哪些是没有办法提前预知的,而各个模块需要哪些数据的时候只需到公共信息中去提取就好了,主程序也不需要知道到底有哪些信息是公用的,它只要负责将消息以键值对的形式保存起来供子程序调用就可以了。