从压缩的HTTP中解组JSON:查找值开头的无效字符



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

我用了几个著名的例子(比如这个)。不幸的是,应用程序无法解析所需的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压缩。然而,目前还不清楚如何在果朗进行减压(见此处)。

Go JSON封送器只能封送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整理器来解析它们。

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

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

我相信这是由于您的双重编码。ioutil.ReadAll(res.Body)返回一个[]byte,所以当您执行[]byte(body)时,您正在铸造已经是字节数组的内容,我猜测第一个字节UTF值是x。只需更新此项;json.Unmarshal([]byte(body), &jsondata)json.Unmarshal(body, &jsondata),我敢打赌它会很好地解组。

此外,与您的错误无关,但正如另一个答案中所指出的,如果您不导出结构中的字段(在go中,这意味着字段名称以大写字母开头),则解组器将无法使用它们。要实现这一点,您需要将键入的内容更新为;

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

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

请尝试

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)
}

并确保CCD_ 9返回json类型。

最新更新