从compessed HTTP中解组JSON:寻找值开头的无效字符

时间:2015-04-07 23:03:56

标签: json http go deflate

我刚刚编写了我的第一个Go应用程序,它通过http下载和解组简单的JSON对象。 Http内容被压缩: 'content-encoding': 'deflate'

我使用了几个众所周知的例子(如this)。不幸的是,应用程序无法解析所需的JSON,并且出现了非常罕见和奇怪的错误。我无法找出问题所在。任何帮助将不胜感激。

JSON输入 (Python用于调试)

In [8]: r = requests.get("http://172.17.0.31:20000/top")

In [9]: r.text
Out[9]: u'{"timestamp":{"tv_sec":1428447555,"tv_usec":600186},"string_timestamp":"2015-04-07 22:59:15.600186","monitor_status":"enabled"}'
In [18]: r.headers
Out[18]: {'content-length': '111', 'content-type': 'application/json', 'connection': 'close', 'content-encoding': 'deflate'}

源代码(根据答案更新

package main

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

type Top struct {
    Timestamp        Timestamp `json:"timestamp"`
    String_timestamp string    `json:"string_timestamp"`
    Monitor_status   string    `json:"monitor_status"`
}

type Timestamp struct {
    Tv_sec  int `json:"tv_sec"`
    Tv_usec int `json:"tv_usec"`
}

func get_content() {

    url := "http://172.17.0.31:20000/top"

    res, err := http.Get(url)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println(res)

    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println(body)

    var jsondata Top
    err = json.Unmarshal(body, &jsondata)
    if err != nil {
        panic(err.Error())
    }

    fmt.Println(jsondata)
}

func main() {
    get_content()
}

错误

[vitaly@thermaltake elliptics-manager]$ go run main.go 
&{200 OK 200 HTTP/1.1 1 1 map[Content-Type:[application/json] Content-Length:[111] Content-Encoding:[deflate]] 0xc20803e340 111 [] true map[] 0xc208028820 <nil>}
[120 156 77 203 65 14 130 48 16 70 225 171 152 127 93 76 59 51 162 244 50 13 96 99 154 216 98 232 192 134 112 119 81 55 110 95 190 183 65 83 142 85 251 252 130 223 160 107 168 113 132 119 66 55 145 182 117 108 62 109 249 70 98 234 108 183 27 84 157 83 121 132 191 19 100 221 165 177 210 216 235 137 200 11 123 230 243 207 195 32 79 37 233 52 135 3 235 82 15 29 75 63 60 227 29 251 27 195 90 38 189]
panic: invalid character 'x' looking for beginning of value

UPD:谢谢大家。现在很明显,这个问题的原因是HTTP响应的deflate压缩。但是,目前还不清楚如何在Golang中执行解压缩(请参阅here)。

4 个答案:

答案 0 :(得分:3)

Go JSON marshaller只能编组unicode字符串。您的JSON似乎不是用unicode编码的,而是使用其他编码(deflate?)。

如果您使用字节流:

[120 156 77 203 65 14 130 48 16 70 225 171 152 127 93 76 59 51 162 244 50 13 96 99 154 216 98 232 192 134 112 119 81 55 110 95 190 183 65 83 142 85 251 252 130 223 160 107 168 113 132 119 66 55 145 182 117 108 62 109 249 70 98 234 108 183 27 84 157 83 121 132 191 19 100 221 165 177 210 216 235 137 200 11 123 230 243 207 195 32 79 37 233 52 135 3 235 82 15 29 75 63 60 227 29 251 27 195 90 38 189]

尝试从中获取unicode字符串:

body := []byte{120, 156, 77, 203, 65, 14, 130, 48, 16, 70, 225, 171, 152, 127, 93, 76, 59, 51, 162, 244, 50, 13, 96, 99, 154, 216, 98, 232, 192, 134, 112, 119, 81, 55, 110, 95, 190, 183, 65, 83, 142, 85, 251, 252, 130, 223, 160, 107, 168, 113, 132, 119, 66, 55, 145, 182, 117, 108, 62, 109, 249, 70, 98, 234, 108, 183, 27, 84, 157, 83, 121, 132, 191, 19, 100, 221, 165, 177, 210, 216, 235, 137, 200, 11, 123, 230, 243, 207, 195, 32, 79, 37, 233, 52, 135, 3, 235, 82, 15, 29, 75, 63, 60, 227, 29, 251, 27, 195, 90, 38, 189}
fmt.Println(string(body))

你会在控制台中看到一个奇怪的(压缩的?)字符串,而不是JSON。

我猜python http客户端会自动解压缩缩减的字节,而Go http客户端则不会解压缩(我知道它对gzip是这样做的,但不确定是否为deflate)。您必须读出缩小的字节并将它们转换为unicode字符串,然后才能使用JSON编组器来解析它们。

答案 1 :(得分:2)

我不知道'x',但结构字段必须是公共的(以大写字母开头)才能被json Unmarshaller考虑。当然,名称与json键不匹配,你必须像这样添加json注释:

type Top struct {
    Timestamp        Timestamp `json:"timestamp"`
    String_timestamp string `json:"string_timestamp"`
    Monitor_status   string `json:"monitor_status"`
}

答案 2 :(得分:1)

我相信这是由于你的双重编码。 ioutil.ReadAll(res.Body)会返回[]byte,所以当您[]byte(body)执行已经是字节数组的操作时,我的猜测是第一个字节的UTF值是x。只需更新一下; json.Unmarshal([]byte(body), &jsondata)json.Unmarshal(body, &jsondata),我打赌它会解散就好了。

此外,与您的错误无关,但如果您不导出结构中的字段(在这里,这意味着使用大写字母开始字段名称),则在另一个答案中指出,然后解组者将无法进行他们的用户。要完成这项工作,您需要将类型更新为;

type Top struct {
    Timestamp        Timestamp `json:"timestamp"`
    String_timestamp string `json:"string_timestamp"`
    Monitor_status   string `json:"monitor_status"`
}

json注释是必需的,因为unmarshaler非常严格,并且要求字段名称完全匹配(区分大小写)。

答案 3 :(得分:1)

请试试这个

func get_content() {

    url := "http://172.17.0.31:20000/top"

    res, err := http.Get(url)
    if err != nil {
        panic(err.Error())
    }
    defer res.Body.Close()

    fmt.Println("res body:", res.Body)

    body, err := ioutil.ReadAll(resp=.Body)

    fmt.Println("body:", body)
    re, err := zlib.NewReader(bytes.NewReader(body))
    fmt.Println("zlib:", re)
    enflated, err := ioutil.ReadAll(re)
    fmt.Println("enflated:", string(enflated))

    var jsondata Top
    err = json.Unmarshal(body, &jsondata)
    if err != nil {
        panic(err.Error())
    }

    fmt.Println(jsondata)
}

并确保http://172.17.0.31:20000/top返回json类型。