du90093662774150 2014-07-10 16:21
浏览 67
已采纳

解组JSON HTTP响应

I have very recently started playing with GO and I am trying to unmarshal a JSON response from http://www.oref.org.il/WarningMessages/alerts.json.
For some reason that I can't understand the unmarshaling failed, the unmarshalled struct is empty (my guess is that it is somehow related to encoding).

Below is the code, any help is appreciated.

Thanks, Itay

package main

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

const alertsUrl = "http://www.oref.org.il/WarningMessages/alerts.json"

type Record struct {
  Id string `json:id`
  Title string `json:title`
  Data []string `json:data`
}

func main() {
  client := &http.Client{}
  req, err := http.NewRequest("GET", alertsUrl, nil)
  perror(err)

  req.Header.Add("Content-Type", "application/json; charset=utf-8")
  res, err := client.Do(req)
  perror(err)

  defer res.Body.Close()
  body, err := ioutil.ReadAll(res.Body)
  perror(err)

  var record Record
  json.Unmarshal(body, &record)
  fmt.Println(record)
}

func perror(err error) {
  if err != nil {
    panic(err)
  }
}
  • 写回答

1条回答 默认 最新

  • dongxuanchao1425 2014-07-10 17:45
    关注

    You're ignoring the error on JSON Unmarshal:

    func Unmarshal(data []byte, v interface{}) error
    

    See that it returns an error. Adding that in,

    err = json.Unmarshal(body, &record)
    perror(err)
    

    It looks like this is a Unicode error — you need to decode the UTF-16 data.

    How should you do this? Take a look at this answer. Basically once you read the body like body, err := ioutil.ReadAll(res.Body), you want to decode the UTF-16 bytes to a string. There is a lot going on there, but we can take some liberties: for instance, pulling up the URL in Chrome, the browser tells us that it is UTF-16 LE. So we can skip the ByteOrder detection. So the key here is this function:

    func UTF16BytesToString(b []byte, o binary.ByteOrder) string {
        utf := make([]uint16, (len(b)+(2-1))/2)
        for i := 0; i+(2-1) < len(b); i += 2 {
            utf[i/2] = o.Uint16(b[i:])
        }
        if len(b)/2 < len(utf) {
            utf[len(utf)-1] = utf8.RuneError
        }
        return string(utf16.Decode(utf))
    }
    

    Knowing our byte order and passing it in, this will convert the naïve byte array to a string of UTF-16 characters. Thanks to user OneOfOne's comment, we can also detect the BOM easily.

    The result:

    package main
    
    import (
        "encoding/binary"
        "encoding/json"
        "fmt"
        "io/ioutil"
        "net/http"
        "unicode/utf16"
        "unicode/utf8"
    )
    
    const alertsUrl = "http://www.oref.org.il/WarningMessages/alerts.json"
    
    type Record struct {
        Id    string   `json:id`
        Title string   `json:title`
        Data  []string `json:data`
    }
    
    // LazyUTF16BytesToString converts UTF-16 encoded bytes, in big or little endian byte order,
    // to a UTF-8 encoded string.
    func LazyUTF16BytesToString(b []byte) string {
        if len(b)%2 != 0 {
            panic("len(b) % 2 != 0")
        }
    
        var codec binary.ByteOrder = binary.LittleEndian
        if b[0] == 0xFE && b[1] == 0xFF { //check and strip the BOM
            b = b[2:]
            codec = binary.BigEndian
        } else if b[0] == 0xFF && b[1] == 0xFE {
            b = b[2:]
        }
    
        utf := make([]uint16, (len(b)+(2-1))/2)
        for i := 0; i+(2-1) < len(b); i += 2 {
            utf[i/2] = codec.Uint16(b[i:])
        }
        if len(b)/2 < len(utf) {
            utf[len(utf)-1] = utf8.RuneError
        }
        return string(utf16.Decode(utf))
    }
    
    func main() {
        client := &http.Client{}
        req, err := http.NewRequest("GET", alertsUrl, nil)
        perror(err)
    
        req.Header.Add("Content-Type", "application/json; charset=utf-8")
        res, err := client.Do(req)
        perror(err)
    
        defer res.Body.Close()
        body, err := ioutil.ReadAll(res.Body)
        perror(err)
    
        bodyString := LazyUTF16BytesToString(body)
    
        var record Record
        err = json.Unmarshal([]byte(bodyString), &record)
        perror(err)
        fmt.Println(record)
    }
    
    func perror(err error) {
        if err != nil {
            panic(err)
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 目前主流的音乐软件,像网易云音乐,QQ音乐他们的前端和后台部分是用的什么技术实现的?求解!
  • ¥60 pb数据库修改与连接
  • ¥15 spss统计中二分类变量和有序变量的相关性分析可以用kendall相关分析吗?
  • ¥15 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab