しばらく止まってたTwitterのフォロワーを増やす手助けするWebアプリケーションの開発を再開したのでそれ関連のメモ。
Go言語でJsonをいい感じに使いたいときに見る用。
今回はTwitterApiでキーワードからUserを検索して帰ってきたJSONをごにょごにょします。
とりあえず受け取る
とりあえず受け取らないと話にならないので受け取る。
RevelのTwitterOauthのサンプルがベースになってます。必要なとこだけ抜粋してあるのでこれだけじゃ動きません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
//必要なものをImport import ( "encoding/json" "io/ioutil" "net/http" "github.com/revel/revel" ) // ここら辺にも色々ある // // キーワードでユーザー検索 // Revelのサンプルにある奴のget先を変えただけ // とりあえずベタ打ちでプログラミングをキーワードとする func (c App) GetSearch(status string) revel.Result { //セッションに保存されているトークンを使ってる。本題じゃないので無視。 user := getUserFromSession(c) if user.AccessToken == nil { return c.Redirect(App.Index) } // We have a token, so look for users. resp, err := TWITTER.Get( "https://api.twitter.com/1.1/users/search.json", map[string]string{"q": "プログラミング", "f": "users", "src": "typd", "count": "3", "page": "0"}, user.AccessToken) if err != nil { revel.ERROR.Println(err) return c.Render() } //deferは便利だなあ defer resp.Body.Close() //http.Responseで結果が返ってくるのでdecode result := decodeToInterfece(resp) //JSONとして出力 return c.RenderJson(result) } //本題はここ func decodeToInterface(response *http.Response) interface{} { //二つ目の返り値はエラーなので本当はハンドリングしてくれよな b, _ := ioutil.ReadAll(response.Body) // decode // interface型にdecodeすると全てを受け入れてくれる //がそのあと使うときに死ぬほどCastする必要があってしんどそう var result interface{} _ = json.Unmarshal(b, &result) return result } |
はい。要すると帰ってきたhttp.ResponseのBodyをjson.Unmarshalでinterface型にデコードしてます。結果としては生のままのJsonが一応resultの中に入った形になります。
出力結果。
resultの中身はこう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
[ { "contributors_enabled": false, "created_at": "Thu Aug 21 01:45:40 +0000 2014", "default_profile": false, "default_profile_image": false, "description": "「初心者から、創れる人を生み出す」 オンラインプログラミング学習サービスProgateの公式アカウントです。Progate→https://t.co/UHAVK1yJCG 会社概要→https://t.co/lCYk9OhxqN トップ画像はProgateのメインキャラクター「にんじゃわんこ」です。", "entities": { "description": { "urls": [ { "display_url": "prog-8.com", "expanded_url": "https://prog-8.com", "indices": [ 62, 85 ], "url": "https://t.co/UHAVK1yJCG" }, { "display_url": "prog-8.com/about", "expanded_url": "https://prog-8.com/about", "indices": [ 91, 114 ], "url": "https://t.co/lCYk9OhxqN" } ] }, "url": { "urls": [ { "display_url": "prog-8.com", "expanded_url": "http://prog-8.com", "indices": [ 0, 23 ], "url": "https://t.co/AkRq5JjUNX" } ] } }, "favourites_count": 14264, "follow_request_sent": false, "followers_count": 6333, "following": false, "friends_count": 1982, "geo_enabled": false, "has_extended_profile": false, "id": 2.750973108e+09, "id_str": "2750973108", "is_translation_enabled": false, "is_translator": false, "lang": "ja", "listed_count": 105, "location": "東京 渋谷区", "name": "Progate@プログラミング学習", "notifications": false, "profile_background_color": "000000", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", "profile_background_tile": false, "profile_banner_url": "https://pbs.twimg.com/profile_banners/2750973108/1422151804", "profile_image_url": "http://pbs.twimg.com/profile_images/846343435061669889/9lhlpzvz_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/846343435061669889/9lhlpzvz_normal.jpg", "profile_link_color": "3B94D9", "profile_sidebar_border_color": "000000", "profile_sidebar_fill_color": "000000", "profile_text_color": "000000", "profile_use_background_image": false, "protected": false, "screen_name": "prog_8", "status": { "contributors": null, "coordinates": null, "created_at": "Fri Apr 28 13:26:33 +0000 2017", "entities": { "hashtags": [], "symbols": [], "urls": [ { "display_url": "ur2.link/DcnS", "expanded_url": "http://ur2.link/DcnS", "indices": [ 96, 119 ], "url": "https://t.co/S4hJuhzNgs" } ], "user_mentions": [ { "id": 8.084849008273285e+17, "id_str": "808484900827328513", "indices": [ 3, 15 ], "name": "修羅観音", "screen_name": "syurakannon" } ] }, "favorite_count": 0, "favorited": false, "geo": null, "id": 8.579491575200563e+17, "id_str": "857949157520056326", "in_reply_to_screen_name": null, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "is_quote_status": false, "lang": "ja", "place": null, "possibly_sensitive": false, "retweet_count": 11, "retweeted": false, "retweeted_status": { "contributors": null, "coordinates": null, "created_at": "Fri Apr 28 04:02:52 +0000 2017", "entities": { "hashtags": [], "symbols": [], "urls": [ { "display_url": "ur2.link/DcnS", "expanded_url": "http://ur2.link/DcnS", "indices": [ 79, 102 ], "url": "https://t.co/S4hJuhzNgs" } ], "user_mentions": [] }, "favorite_count": 15, "favorited": false, "geo": null, "id": 8.578073005006602e+17, "id_str": "857807300500660224", "in_reply_to_screen_name": null, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "is_quote_status": false, "lang": "ja", "place": null, "possibly_sensitive": false, "retweet_count": 11, "retweeted": false, "source": "\u003ca href=\"http://twitter.com\" rel=\"nofollow\"\u003eTwitter Web Client\u003c/a\u003e", "text": "Progateのリニューアル、特にRuby on Railsコースの演習画面が、タブブラウザのような形で、画面を広く使えるようになった事は、凄く有り難し。合掌https://t.co/S4hJuhzNgs", "truncated": false }, "source": "\u003ca href=\"http://twitter.com/download/iphone\" rel=\"nofollow\"\u003eTwitter for iPhone\u003c/a\u003e", "text": "RT @syurakannon: Progateのリニューアル、特にRuby on Railsコースの演習画面が、タブブラウザのような形で、画面を広く使えるようになった事は、凄く有り難し。合掌https://t.co/S4hJuhzNgs", "truncated": false }, "statuses_count": 2296, "time_zone": null, "translator_type": "none", "url": "https://t.co/AkRq5JjUNX", "utc_offset": null, "verified": false }, { "contributors_enabled": false, "created_at": "Fri Sep 05 11:03:21 +0000 2008", "default_profile": true, "default_profile_image": false, "description": "プログラミング が大好きです", "entities": { "description": { "urls": [] } }, "favourites_count": 27252, "follow_request_sent": false, "followers_count": 2744, "following": true, "friends_count": 595, "geo_enabled": false, "has_extended_profile": false, "id": 1.6142545e+07, "id_str": "16142545", "is_translation_enabled": false, "is_translator": false, "lang": "ja", "listed_count": 298, "location": "☕", "name": "プログラミング だけが恋人", "notifications": false, "profile_background_color": "C0DEED", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", "profile_background_tile": false, "profile_image_url": "http://pbs.twimg.com/profile_images/420912481272217600/l1z_l2Wt_normal.png", "profile_image_url_https": "https://pbs.twimg.com/profile_images/420912481272217600/l1z_l2Wt_normal.png", "profile_link_color": "1DA1F2", "profile_sidebar_border_color": "C0DEED", "profile_sidebar_fill_color": "DDEEF6", "profile_text_color": "333333", "profile_use_background_image": true, "protected": false, "screen_name": "adakoda", "status": { "contributors": null, "coordinates": null, "created_at": "Fri May 05 08:44:29 +0000 2017", "entities": { "hashtags": [], "symbols": [], "urls": [], "user_mentions": [ { "id": 8.379212e+06, "id_str": "8379212", "indices": [ 3, 9 ], "name": "竹内裕昭", "screen_name": "takke" } ] }, "favorite_count": 0, "favorited": false, "geo": null, "id": 8.604148864545464e+17, "id_str": "860414886454546432", "in_reply_to_screen_name": null, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "is_quote_status": false, "lang": "ja", "place": null, "retweet_count": 30, "retweeted": false, "retweeted_status": { "contributors": null, "coordinates": null, "created_at": "Fri May 05 07:16:46 +0000 2017", "entities": { "hashtags": [], "symbols": [], "urls": [], "user_mentions": [] }, "favorite_count": 25, "favorited": false, "geo": null, "id": 8.603928126085939e+17, "id_str": "860392812608593920", "in_reply_to_screen_name": null, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "is_quote_status": false, "lang": "ja", "place": null, "retweet_count": 30, "retweeted": false, "source": "\u003ca href=\"http://www.twitpane.com\" rel=\"nofollow\"\u003eTwitPane Android\u003c/a\u003e", "text": "Android4.x端末で繋がらないので☆1です → 分かりました、サポート対象外にしますね!", "truncated": false }, "source": "\u003ca href=\"http://twitter.com/download/android\" rel=\"nofollow\"\u003eTwitter for Android\u003c/a\u003e", "text": "RT @takke: Android4.x端末で繋がらないので☆1です → 分かりました、サポート対象外にしますね!", "truncated": false }, "statuses_count": 71283, "time_zone": "Tokyo", "translator_type": "none", "url": null, "utc_offset": 32400, "verified": false }, { "contributors_enabled": false, "created_at": "Thu Jul 03 21:43:34 +0000 2008", "default_profile": false, "default_profile_image": false, "description": "AtCoder(株)代表取締役社長(競技プログラミングの会社)/筑駒中高→慶應SFC/競プロ世界ランカー(実績:https://t.co/x50mGcr6h2 ICFPC優勝3回等)/たこやき/ぷよぷよ/モバマスまゆ小日向でしてP まゆドリフ全一/書籍『最強最速アルゴリズマー養成講座』著者", "entities": { "description": { "urls": [ { "display_url": "chokudai.net", "expanded_url": "http://chokudai.net", "indices": [ 56, 79 ], "url": "https://t.co/x50mGcr6h2" } ] }, "url": { "urls": [ { "display_url": "chokudai.hatenablog.com", "expanded_url": "http://chokudai.hatenablog.com/", "indices": [ 0, 22 ], "url": "http://t.co/3temA9ZeaS" } ] } }, "favourites_count": 8543, "follow_request_sent": false, "followers_count": 17563, "following": false, "friends_count": 11821, "geo_enabled": false, "has_extended_profile": true, "id": 1.53129e+07, "id_str": "15312900", "is_translation_enabled": false, "is_translator": false, "lang": "ja", "listed_count": 885, "location": "東京都", "name": "chokudai(高橋 直大)", "notifications": false, "profile_background_color": "000000", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", "profile_background_tile": false, "profile_banner_url": "https://pbs.twimg.com/profile_banners/15312900/1474974082", "profile_image_url": "http://pbs.twimg.com/profile_images/668994829238464513/RH2Z8jzP_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/668994829238464513/RH2Z8jzP_normal.jpg", "profile_link_color": "3B94D9", "profile_sidebar_border_color": "000000", "profile_sidebar_fill_color": "000000", "profile_text_color": "000000", "profile_use_background_image": false, "protected": false, "screen_name": "chokudai", "status": { "contributors": null, "coordinates": null, "created_at": "Fri May 05 08:56:24 +0000 2017", "entities": { "hashtags": [], "symbols": [], "urls": [], "user_mentions": [] }, "favorite_count": 3, "favorited": false, "geo": null, "id": 8.604178845094502e+17, "id_str": "860417884509450241", "in_reply_to_screen_name": null, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "is_quote_status": false, "lang": "ja", "place": null, "retweet_count": 0, "retweeted": false, "source": "\u003ca href=\"http://twitter.com\" rel=\"nofollow\"\u003eTwitter Web Client\u003c/a\u003e", "text": "実は自分は正規分布のランダム値殆ど使ったことがなくって、そのかわり0から1の一様分布を適当に累乗したものを使うことが多いんだけど、やっぱちゃんと正規分布使うともうちょっと上手いこといったりするのかな。", "truncated": false }, "statuses_count": 74884, "time_zone": "Tokyo", "translator_type": "none", "url": "http://t.co/3temA9ZeaS", "utc_offset": 32400, "verified": true } ] |
長えよちくしょう
3人だけでこの長さ。まあ全部だしね。
で、これだと使うのが大変だし要らない情報があまりにも多いのでInterface型ではなく、任意の構造体にDecodeしてやりましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//decodeToInterfaceをこれに置き換えてやる func decodeSearchUser(response *http.Response) []userData { result := []userData{} err := json.NewDecoder(response.Body).Decode(&result) if err != nil { revel.ERROR.Println(err) } return result } //任意の構造体。とりあえず名前と詳細だけとってきてみる type userData struct { Name string `json:"name"`//これでjsonのキー名と紐付ける Description string `json:"description"`//タグと呼ぶ } |
こんな感じ。
- 任意の構造体を宣言する。
- JSONのキー名をタグで指定
- デコード
ですね。
ポイントはタグですかね。
変数名のあとにこれ「joson:"キー名"
」を付けるとキー名と変数を関連づけてくれます。ダブルクォーテーション忘れずに。
で、decodeToInterfaceをこっちに置き換えてやると結果は
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[ { "name": "Progate@プログラミング学習", "description": "「初心者から、創れる人を生み出す」 オンラインプログラミング学習サービスProgateの公式アカウントです。Progate→https://t.co/UHAVK1yJCG 会社概要→https://t.co/lCYk9OhxqN トップ画像はProgateのメインキャラクター「にんじゃわんこ」です。" }, { "name": "プログラミング だけが恋人", "description": "プログラミング が大好きです" }, { "name": "chokudai(高橋 直大)", "description": "AtCoder(株)代表取締役社長(競技プログラミングの会社)/筑駒中高→慶應SFC/競プロ世界ランカー(実績:https://t.co/x50mGcr6h2 ICFPC優勝3回等)/たこやき/ぷよぷよ/モバマスまゆ小日向でしてP まゆドリフ全一/書籍『最強最速アルゴリズマー養成講座』著者" } ] |
あ〜無駄がないんじゃ〜
ということで必要なフィールドだけ増やしていけばすっきりとすみそうです。
そのうちこれをAjaxで受け取ってtypeScriptでごにょごにょしたりする気がします。
ということであんまりまとまってなくてごめんなさいなメモでした。
ではまた。