JavaScriptを有効にしてください

Go言語でJsonをいい感じに使う

 ·  ☕ 7 分で読めます  ·  ✍️ saiki

しばらく止まってたTwitterのフォロワーを増やす手助けするWebアプリケーションの開発を再開したのでそれ関連のメモ。

Go言語でJsonをいい感じに使いたいときに見る用。

今回はTwitterApiでキーワードからUserを検索して帰ってきたJSONをごにょごにょします。

とりあえず受け取る

とりあえず受け取らないと話にならないので受け取る。

RevelのTwitterOauthのサンプルがベースになってます。必要なとこだけ抜粋してあるのでこれだけじゃ動きません。

//必要なものを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の中身はこう。

[
  {
    "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してやりましょう。

//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"`//タグと呼ぶ
}

こんな感じ。

  1. 任意の構造体を宣言する。
  2. JSONのキー名をタグで指定
  3. デコード

ですね。

ポイントはタグですかね。

変数名のあとにこれ「`joson:”キー名”`」を付けるとキー名と変数を関連づけてくれます。ダブルクォーテーション忘れずに。

で、decodeToInterfaceをこっちに置き換えてやると結果は

 

[
  {
    "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でごにょごにょしたりする気がします。

 

ということであんまりまとまってなくてごめんなさいなメモでした。

ではまた。

 

共有

saiki
著者
saiki
Android App Developper