5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

golangにおけるJSONPの解析

Posted at

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しましょう:sweat:...という記事です。

例)

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文字列の先頭から比較して削除しています。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?