diff --git a/7.1.md b/7.1.md index 0dd171a2..fd98cefb 100644 --- a/7.1.md +++ b/7.1.md @@ -108,13 +108,13 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的 127.0.0.2 -- 如果struct中有一个字段类型xml.Name,叫做XMLName,那么在解析的时候就会保存这个element的名字到该字段,如上面例子中的servers。 -- 如果struct里面字段后面的tag里面定义了XML的element名称,那么解析的时候就会把相应的element值赋值给struct字段,如上servername和serverip定义。 -- 如果struct里面字段后面的tag定义了`",attr"`,那么会读取该结构所对应的element下面的属性字段,如上version定义。 -- 如果struct字段后面的tag定义了`"a>b>c"`,那么会解析xml元素的结构a下面的b下面的c元素。 -- 如果struct字段后面的tag定义了`"-"`,那么这个字段不会被解析到任何数据。 +- 如果struct中有一个叫做XMLName,且类型为xml.Name字段,那么在解析的时候就会保存这个element的名字到该字段,如上面例子中的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"`,如果他的子元素在不满足其他的规则的时候就会匹配到这个字段。 -- 如果struct字段后面的tag定义了`",comments"`,这个字段一般都是[]byte或者string类型,那么在这个元素下面的注释会累积存储在这个字段里面。 +- 如果某个XML元素包含一条或者多条注释,那么这些注释将被累加到第一个tag含有",comments"的字段上,这个字段的类型可能是[]byte或string,如果没有这样的字段存在,那么注释将会被抛弃。 上面详细讲述了如何定义struct的tag。 只要设置对了tag,那么XML解析就如上面示例般简单,tag和XML的element是一一对应的关系,如上所示,我们还可以通过slice来表示多个同级元素。 @@ -177,31 +177,31 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的 和我们之前定义的文件的格式一模一样,之所以会有`os.Stdout.Write([]byte(xml.Header))` 这句代码的出现,是因为`xml.MarshalIndent`或者`xml.Marshal`输出的信息都是不带XML头的,为了生成正确的xml文件,我们使用了xml包预定义的Header变量。 -我们看到`Marshal`函数接收的参数v是interface类型,所以他可以是任意类型,那么如何生成相应的XML呢? +我们看到`Marshal`函数接收的参数v是interface{}类型的,即它可以接受任意类型的参数,那么xml包,根据什么规则来生成相应的XML文件呢? - 如果v是 array或者slice,那么输出每一个元素,类似value - 如果v是指针,那么会Marshal指针指向的内容,如果指针为空,什么都不输出 - 如果v是interface,那么就处理interface所包含的数据 - 如果v是其他数据类型,就会输出这个数据类型所拥有的字段信息 -那么生成的XML文件中的element名字是通过获取的呢?按照如下优先级获取: +生成的XML文件中的element的名字又是根据什么决定的呢?元素名按照如下优先级从struct中获取: - 如果v是struct,XMLName的tag中定义的名称 -- 字段名XMLName,并且类型是xml.Name的字段值 -- 通过strcut的字段中定义的tag来获取 +- 类型为xml.Name的名叫XMLName的字段的值 +- 通过strcut中字段的tag来获取 - 通过strcut的字段名用来获取 - marshall的类型名称 -而那么里面的结构和数据是如何输出的呢?我们需要如何设置struct里面的tag信息呢? +我们应如何设置struct 中字段的tag信息以控制最终xml文件的生成呢? - XMLName不会被输出 - tag中含有`"-"`的字段不会输出 - tag中含有`"name,attr"`,会以name作为属性名,字段值作为值输出为这个XML元素的属性,如上version字段所描述 - tag中含有`",attr"`,会以这个struct的字段名作为属性名输出为XML元素的属性,类似上一条,只是这个name默认是字段名了。 -- tag中含有`",chardata"`,写为xml的data数据 -- tag中含有`",innerxml"`,里面保存的是所有数据,而不会输出到XML -- tag中含有`",comment"`,这个用来写XML的注释 -- tag中含有`"omitempty"`,这是是如果该字段的值为空值那么就不会被输出到XML,空值包括:false、0、nil或者"" +- 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,例如如下代码就会输出 FirstName string `xml:"name>first"` @@ -213,7 +213,7 @@ Go语言的反射机制,可以利用这些tag信息来将来自XML文件中的 -上面我们介绍了如何使用Go语言的xml包来编/解码XML文件,重要的一点是对XML的所有操作都是通过struct tag来实现的,所以学会对struct tag的运用变得非常重要,在文章中我们简要的列举了如何定义tag。更多内容请参看相应的官方资料。 +上面我们介绍了如何使用Go语言的xml包来编/解码XML文件,重要的一点是对XML的所有操作都是通过struct tag来实现的,所以学会对struct tag的运用变得非常重要,在文章中我们简要的列举了如何定义tag。更多内容或tag定义请参看相应的官方资料。 ## links * [目录]() diff --git a/7.2.md b/7.2.md index 765dd2de..f051608e 100644 --- a/7.2.md +++ b/7.2.md @@ -1,11 +1,11 @@ #7.2 JSON处理 -JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于让人阅读。尽管JSON是在Javascript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯。JSON与XML最大的不同在于XML是一个完整的标记语言,而JSON不是。JSON由于比XML更小、更快,更易解析,以及浏览器的内建快速解析支持,使得其更适用于网络数据传输领域。目前我们看到很多的开放平台,基本上所有的接口都是采用了JSON作为他们的数据交互。那么JSON在Web开发中如此重要,Go语言对于JSON支持的怎么样呢?其实Go语言的标准库里面已经非常好的支持了JSON,可以对JSON包进行解析、生成JSON数据。 +JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于让人阅读。尽管JSON是Javascript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯。JSON与XML最大的不同在于XML是一个完整的标记语言,而JSON不是。JSON由于比XML更小、更快,更易解析,以及浏览器的内建快速解析支持,使得其更适用于网络数据传输领域。目前我们看到很多的开放平台,基本上都是采用了JSON作为他们的数据交互的接口。既然JSON在Web开发中如此重要,那么Go语言对JSON支持的怎么样呢?Go语言的标准库已经非常好的支持了JSON,可以很容易的对JSON数据进行编、解码的工作。 -我们还是假设目前想要描述所有的服务器列表,通过JSON如何来表达,请看下面的描述 +前一小节的运维的例子用json来表示,结果描述如下: {"servers":[{"serverName":"Shanghai_VPN","serverIP":"127.0.0.1"},{"serverName":"Beijing_VPN","serverIP":"127.0.0.2"}]} -接下来的例子以此JSON数据为基础,我们来进行JSON的解析和生成。 +本小节余下的内容将以此JSON数据为基础,来介绍go语言的json包对JSON数据的编、解码。 ##解析JSON ###解析到结构体 @@ -38,37 +38,18 @@ JSON(Javascript Object Notation)是一种轻量级的数据交换语言, fmt.Println(s) } -通过上面的例子我们可以看到我们首先定义了结构体,结构体和JSON的数据一一对应,数组对应slice,字段名对应JSON里面的KEY,那么解析的时候如何解析到对应的字段的呢?例如JSON的key是`Foo`,那么怎么找对应的字段呢? +通在上面的示例代码中,我们首先定义了与json数据对应的结构体,数组对应slice,字段名对应JSON里面的KEY,在解析的时候,如何将json数据与struct字段相匹配呢?例如JSON的key是`Foo`,那么怎么找对应的字段呢? -- 首先查找字段的tag里面带有`Foo`的导出字段(首字母大写) +- 首先查找tag含有`Foo`的可导出的struct字段(首字母大写) - 其次查找字段名是`Foo`的导出字段 - 最后查找类似`FOO`或者`FoO`这样的除了首字母之外其他大小写不敏感的导出字段 -聪明的你一定注意到了一点,能够输出的数据必须是导出字段,其他字段是不能输出的。同时JSON解析的时候只会解析能找得到的字段,如果找不到的字段会被忽略,这样的一个好处是在于当你接收到一个很大的JSON数据的时候,你如果只想部分数据,那么用这种方式就可以轻松的解决了。 - -上面这个是官方提供的解决方案,其实很多时候我们通过类型断言,操作起来不是很方便,目前bitly公司开发了一个`simplejson`,在处理未知结构体的JSON处理中相当方便,详细例子如下所示: - - js, err := NewJson([]byte(`{ - "test": { - "array": [1, "2", 3], - "int": 10, - "float": 5.150, - "bignum": 9223372036854775807, - "string": "simplejson", - "bool": true - } - }`)) - - arr, _ := js.Get("test").Get("array").Array() - i, _ := js.Get("test").Get("int").Int() - ms := js.Get("test").Get("string").MustString() - -我们看到通过这个库对于我们来说操作JSON非常的简单,比起前面介绍的官方方案更加简洁。 +聪明的你一定注意到了这一点:能够被赋值的字段必须是可导出字段(即首字母大写)。同时JSON解析的时候只会解析能找得到的字段,如果找不到的字段会被忽略,这样的一个好处是:当你接收到一个很大的JSON数据结构而你却只想获取其中的部分数据的时候,你只需将你想要的数据对应的字段名大写,即可轻松解决这个问题。 ###解析到interface -我们知道上面哪种解析方式是当我们了解了JSON的数据结构的情况下来进行的解析,那么如果我们在不知道JSON格式的情况下,如何来解析JSON呢? +上面那种解析方式是在我们知晓被解析的JSON数据的结构的前提下采取的方案,如果我们不知道被解析的数据的格式,又应该如何来解析呢? -我们知道interface{}可以存储任意的数据类型,那么这正好符合JSON包在未知数据结构的时候来进行解析,JSON包中采用map[string]interface{}和[]interface{}结构来存储任意的JSON对象和数组。Go类型和JSON类型的对应关系如下: +我们知道interface{}可以用来存储任意数据类型的对象,这种数据结构正好用于存储解析的未知结构的json数据的结果。JSON包中采用map[string]interface{}和[]interface{}结构来存储任意的JSON对象和数组。Go类型和JSON类型的对应关系如下: - bool 代表 JSON booleans, - float64 代表 JSON numbers, @@ -116,7 +97,26 @@ JSON(Javascript Object Notation)是一种轻量级的数据交换语言, fmt.Println(k, "is of a type I don't know how to handle") } } -通过上面的示例代码我们看到,我们就可以来访问结构不确定的JSON数据串。 +通过上面的示例可以看到,通过interface{}与type assert的配合,我们就可以解析未知结构的JSON数了。 + +上面这个是官方提供的解决方案,其实很多时候我们通过类型断言,操作起来不是很方便,目前bitly公司开源了一个叫做`simplejson`的包,在处理未知结构体的JSON时相当方便,详细例子如下所示: + + js, err := NewJson([]byte(`{ + "test": { + "array": [1, "2", 3], + "int": 10, + "float": 5.150, + "bignum": 9223372036854775807, + "string": "simplejson", + "bool": true + } + }`)) + + arr, _ := js.Get("test").Get("array").Array() + i, _ := js.Get("test").Get("int").Int() + ms := js.Get("test").Get("string").MustString() + +可以看到,使用这个库操作JSON比起官方包来说,简单的多,详细的请参考如下地址:https://github.com/bitly/go-simplejson ##生成JSON 我们开发很多应用的时候,最后都是要输出JSON数据串,那么如何来处理呢?JSON包里面通过`Marshal`函数来处理,函数定义如下: @@ -167,9 +167,9 @@ JSON(Javascript Object Notation)是一种轻量级的数据交换语言, Servers []Server `json:"servers"` } -通过修改上面的结构体定义,这样输出的JSON串和我们最开始定义的JSON串就保持一模一样了。 +通过修改上面的结构体定义,输出的JSON串就和我们最开始定义的JSON串保持一致了。 -针对JSON的输出我们struct tag的定义需要注意一下几点设置: +针对JSON的输出,我们在定义struct tag的时候需要注意的几点是: - tag中带有`"-"`,那么这个字段不会输出到JSON - tag中带有自定义名称,那么这个自定义名称会出现在JSON的字段名中,例如上面例子中serverName @@ -184,7 +184,7 @@ Marshal函数只有在转换成功的时候才会返回数据,在转换的过 - 指针在编码的时候会输出指针指向的内容,而空指针会输出null -通过上面这个讲解,我们了解了如何使用Go语言的标准包里面的JSON来进行编解JSON数据,同时介绍了第三方包`go-simplejson`如何方便的操作JSON数据,这个对于我们接下来的Web开发相当重要。 +本小节,我们介绍了如何使用Go语言的json标准包来编解码JSON数据,同时也简要介绍了如何使用第三方包`go-simplejson`来在一些情况下简化操作,学会并熟练运用它们将对我们接下来的Web开发相当重要。 ## links * [目录]()