2014-07-10 76 views
1

我最近開始玩GO,我試圖從http://www.oref.org.il/WarningMessages/alerts.json解組JSON響應。
由於某些原因,我無法理解unmarshaling失敗,unmarshalled結構是空的(我的猜測是它與編碼有關)。解組一個JSON http響應

下面是代碼,任何幫助表示讚賞。

感謝, 伊泰

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

** **停止忽略的錯誤,曾經問一個問題就到這裏之前,檢查所有的錯誤。 – Wessie

+0

@Wessie你介意告訴我我忽略了哪個錯誤? –

+0

啊......好吧,我不是很熟悉。再次檢查我可以看到Unmarshal方法在其簽名中返回了一個錯誤。我會嘗試走這條路。 –

回答

3

你無視JSON Unmarshal錯誤:

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

看到它返回一個錯誤。再加上,

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

看起來這是一個Unicode錯誤 - 你需要decode the UTF-16 data

你應該怎麼做?看看this answer。基本上,一旦您讀取了像body, err := ioutil.ReadAll(res.Body)這樣的主體,您想要將UTF-16字節解碼爲一個字符串。這裏有很多事情要做,但我們可以採取一些自由行爲:例如,在Chrome中拉起URL,瀏覽器告訴我們它是UTF-16 LE。所以我們可以跳過ByteOrder檢測。所以,這裏的關鍵是這樣的功能:

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

知道我們的字節順序,並通過它,這將天真的字節數組轉換到UTF-16字符的字符串。感謝用戶OneOfOne的comment,我們也可以輕鬆檢測BOM。

結果:

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

謝謝,我會嘗試 –

+0

所以... Unmarshal方法獲取一個字節數組。解碼結果怎樣才能解組? –

+1

您需要在解組之前進行解碼。添加了細節。 – pswaminathan