Go语言 解组JSON返回错误:查找值开头的字符“\x1f”无效

rryofs0p  于 2023-02-27  发布在  Go
关注(0)|答案(2)|浏览(288)

我正在尝试编写一个命令行实用程序,它将发送一个HTTP请求,并在终端中打印响应正文。

{"arr":[{"name":"Google","url":"https://www.google.com/"},{"name":"Instagram","url":"https://www.instagram.com/"},{"name":"Pinterest","url":"https://www.pinterest.com/"},{"name":"YouTube","url":"https://www.youtube.com/"}]}

这是我的代码,body是字节数组,它是响应主体

fmt.Println(body)

var dat map[string]interface{}
if err := json.Unmarshal(body, &dat); err != nil {
    panic(err)
}
fmt.Println(dat)

Println(body)函数返回

[31 139 8 0 0 0 0 0 0 3 170 86 74 44 42 82 178 138 174 86 202 75 204 77 85 178 82 114 207 207 79 207 73 85 210 81 42 45 202 81 178 82 202 40 41 
41 40 182 210 215 47 47 47 215 75 7 75 233 37 231 231 234 43 213 234 192 117 120 230 21 151 36 166 23 37 230 98 213 148 9 147 197 208 23 144 153 87 146 90 148 90 92 130 85 95 1 76 22 67 95 100 126 105 72 105 18 118 39 86 230 151 150 148 38 193 220 24 91 11 0 0 0 255 255 3 0 64 164 107 195 223 0 0 0]

然后我得到了这个错误

panic: invalid character '\x1f' looking for beginning of value

我是不是误解了json.unmarshall函数?我怎样才能解码那个字节数组,并将得到的json对象打印到终端?
下面是完整的代码

package main 

import (
    "flag"
    "fmt"
    "os"
    "net"
    "bufio"
    "strings"
    "strconv"
    "net/url"
    "encoding/json"
    "io"
)

func main() {
    
    urlPtr := flag.String("url", "", "URL to fetch. (required)")
    flag.Parse()

    if (*urlPtr == "") {
        flag.PrintDefaults()
        os.Exit(1)
    }

    u, err := url.Parse(*urlPtr)

    if err != nil {
        fmt.Println("Error parsing url")
        os.Exit(1)
    }

    path := u.Path
    hostname := u.Hostname()
    
    conn, err := net.Dial("tcp", hostname + ":80")
    if err != nil {
        fmt.Println("Error setting up tcp connection")
        os.Exit(1)
    }

    if path == "" {
        path = "/"
    }

    request := "GET " + path + " HTTP/1.1\r\n" + 
                "Host: " + hostname + ":80\r\n" + 
                "User-Agent: Go-Runtime\r\n" + 
                "Accept: */*\r\n" +
                "Accept-Encoding: gzip, deflate, br\r\n" + 
                "Connection: keep-alive\r\n\r\n"

    _, err = fmt.Fprintf(conn, request)
    if err != nil {
        fmt.Println("Error sending request to serrver")
        os.Exit(1)
    }

    r := bufio.NewReader(conn)
    var content_length int
    content_bool := false
    transfer_encoding := false 
    for {
        line, err := r.ReadString('\n')
        
        if err != nil {
            fmt.Println("Error reading header line")
            os.Exit(1)
        }

        header := strings.Split(line, ": ")      
        if header[0] == "Content-Length" {
            content_length, err = strconv.Atoi(header[1][:len(header[1]) - 2])
            if err != nil {
                fmt.Println("Error reading content length")
                os.Exit(1)
            }
            content_bool = true
        }

        if (header[0] == "Transfer-Encoding") {
            transfer_encoding = true
        }
        
        if line == "\r\n" {
            break
        }
    }

    var body []byte
    var n int

    if content_bool == true {
        body = make([]byte, content_length)
        n, err = r.Read(body)
        if err != nil {
            fmt.Println("Error reading body")
            os.Exit(1)
        }
        fmt.Println(string(body[:n]))
    }

    if transfer_encoding == true {
        
        for {
            line, err := r.ReadString('\n')
            if err != nil {
                fmt.Println("Error reading length of chunk")
                os.Exit(1)
            }
            num := line[:len(line) - 2]
            if part_length, err := strconv.ParseInt(num, 16, 64); err == nil {

                if part_length <= 0 {
                    break
                }
                body_part := make([]byte, part_length)
                if _, err := io.ReadFull(r, body_part); err != nil {
                    fmt.Println("Error reading chunk data")
                    os.Exit(1)
                }
                body = append(body, body_part...)
                _, err = r.Discard(2)
                if err != nil {
                    fmt.Println("Failed to discard trailing CRLF of chunk")
                    os.Exit(1)
                }
            } else {
                break
            }
        }

        fmt.Println(body)

        var dat map[string]interface{}
        if err := json.Unmarshal(body, &dat); err != nil {
            panic(err)
        }
        fmt.Println(dat)
        
    }

}
oipij1gg

oipij1gg1#

\x1f是“单元分隔符”-ASCII表下部的 * 控制 * 字符。
我不知道它是如何在响应中结束的,但我可能会猜测端点可能会在一行中向您发送多个JSON文档-用0x 1F分隔它们,也就是说,做了一个变体。
另一种可能性是您在获取数据时出错。您可以向我们显示代码,以便我们能够做出更好的猜测。

0h4hbjxa

0h4hbjxa2#

出现此错误的原因是您添加了请求标头“Accept-Encoding:gzip,压缩,br\r\n”。
因此gzip是响应编码的第一优先级。
您可以通过阅读头中的content-encoding来检查响应编码。
第一个字节“\x1f”实际上是gzip内容的2个魔术字节“0x 1f 8b”中的1个。
因此,要处理gzip,您需要在以纯文本形式阅读响应之前将其解压缩。
下面是示例代码:

// Check if the response is encoded in gzip format
    if resp.Header.Get("Content-Encoding") == "gzip" {
        reader, err := gzip.NewReader(resp.Body)
        if err != nil {
            panic(err)
        }
        defer reader.Close()
        // Read the decompressed response body
        body, err := io.ReadAll(reader)
        if err != nil {
            panic(err)
        }
        // Do something with the response body
        fmt.Println(string(body))
    } else {
        // The response is not gzip encoded, so read it directly
        body, err := io.ReadAll(resp.Body)
        if err != nil {
            panic(err)
        }
        // Do something with the response body
        fmt.Println(string(body))
    }
}

相关问题