How would I parse JSONP!!
https://www.reddit.com/r/golang/comments/a1oqxp/how_would_i_parse_jsonp/
上記は、
callback関数に囲まれたJSONPの形で返ってくるんですけど......
Unmarshalできなくて困るんですけど......
どうやってJSONP
to JSON
to Struct
しましょう...という記事です。
例)
func main() {
jsonpStr := `
callback({
"hello": "world!!",
"num": 123456
});`
var v interface{}
err := json.Unmarshal(jsonpStr, &v)
fmt.Println(err)
// invalid character 'c' looking for beginning of value
// JSONを期待しているのに先頭にcallbackという不要な文字列があるためUnmarshalがエラーを返します。
}
対応方法
一番上に乗せたreddit
の投稿における回答より二つ紹介します。
titpetricさんの対応方法
先頭から(
までと、後ろから)
までをtrimしちゃえYO!という方法です。
jsonpStr := `callback({
"hello": "world!!",
"num": 123456
});`
jsonStr := jsonpStr[strings.Index(jsonpStr, "(")+1 : strings.Index(jsonpStr, ")")]
fmt.Println(jsonStr)
// {
// "hello": "world!!",
// "num": 123456
// }
var v interface{}
json.Unmarshal([]byte(jsonStr), &v)
fmt.Printf("%+v", v)
// map[hello:world!! num:123456]
jerfさんの対応方法
io.ReaderのWrapperとして用意しようYO!という方法です。
type JSONPWrapper struct {
Prefix string
Underlying io.Reader
gotPrefix bool
}
func (jpw *JSONPWrapper) Read(b []byte) (int, error) {
if jpw.gotPrefix {
return jpw.Underlying.Read(b)
}
prefix := make([]byte, len(jpw.Prefix))
n, err := io.ReadFull(jpw.Underlying, prefix)
if err != nil {
return n, err
}
if string(prefix) != jpw.Prefix {
return n, fmt.Errorf("JSONP prefix mismatch: expected %q, got %q",
jpw.Prefix, prefix)
}
// read until the (; in general, this should just be one read
char := make([]byte, 1)
for char[0] != '(' {
n, err = jpw.Underlying.Read(char)
if n == 0 || err != nil {
return n, err
}
}
// We've now consumed the JSONP prefix.
jpw.gotPrefix = true
return jpw.Underlying.Read(b)
}
func main() {
jsonpStr := `callback({
"hello": "world!!",
"num": 123456
});`
prefix := "callback"
jsonp := bytes.NewBuffer([]byte(jsonpStr))
var decoded interface{}
decoder := json.NewDecoder(&JSONPWrapper{Prefix: prefix, Underlying: jsonp})
// This code depends on the fact the JSON parser stops when it
// finishes a JSON object, so we don't have to handle the concluding
// paren.
decoder.Decode(&decoded)
fmt.Println(decoded)
// map[hello:world!! num:123456]
}
json.NewDecoder
の引数にJSONPWrapper(io.Readerインターフェースを満たした)
を渡すことにて、
decoder.Decode
のタイミングにてJSONWrapper.Read()
が実行されます。
JSONWrapper.Read()
の中では、Prefix
に設定された文字列をjsonp
文字列の先頭から比較して削除しています。