VueとGoでWeb API取得JSONを構造体に詰め込み、V-forと組み合わせ一覧表示してみる話

2021-07-03

はじめに

夏のボーナスで関西の巨大私鉄である近鉄ホールディングスの株を買いました。 昔は近鉄バッファローズという球団を持つほどでしたが… 近鉄にはお世話になっており、通勤通学、難波や梅田に出る際には大変お世話になりました。

台風の日も、大雪の日も止まらず走ってくれましたね。 近鉄が運行停止の日は未曾有の大災害があったときぐらいというのは関西人の常識だと思います。

さて、株を買うにあたって今の所判断材料がチャートぐらいしか無く あがるかなー、上がってほしいなぁ という勘に頼るところが大きくここらで武器が欲しいと思い立ったのがことの始まりです。 何冊か本を読み始めると口を揃えて、有価証券報告書を読みましょうと書かれていう本に当たります。法律で義務付けられている資料だけあって良いことも、悪いことも全て集約されているようです。

ただ、記載量がめぼしい会社の記事を読むだけでも大変で株選びに使うには程遠いです。 こんなのではいつまで経っても株なんて見つけれない。

そこで、有価証券報告書の情報を集約して可視化する所から始める必要があります。 本の内容が真であれば、買うべき株を機械的にフィルタリングできそうだと考えています。 ※過去数年間の純利益、自己保有資産の推移などで足切りができそう。

その取っ掛かりを作りたいなというモチベーションです。(寄り道しながらのんびり作っていきます)。

今回作るもの

過去の記事でGolang使ったREST APIの作り方と、 EDINET APIから有価証券報告書などの資料情報を取得する方法について調べていました。

今回は、Goから構造体をVueに渡してv-forで表示する方法について記載します。 Web APIからJSONを取得してその値を構造体に詰め込んで、Vueで表示する方法について記載します。

過去記事

Go+Vueを組み合わせたアプリテンプレート

EDINETでオススメ株探しツールに向けた第一歩(EDINET APIを使ってみた)

今回は、これらを組み合わせてEDINET APIの取得結果をVueで一覧表示してみるところを進めます。

余談

このアプリを作るためという訳ではないですが、Vueに関しては数ヶ月前にUdemyでこの講座を履修してきました。

超Vue.js 2 完全パック (Vue Router, Vuex含む)

「触ることに抵抗が無いので調べたらなんとか分かりそうな気がする」レベルの理解度ですが進めていきましょう。間違いやこうしたら良いですよというコメントがありましたらお待ちしております。

フォルダ構成

Githubにソースコードも上げておきます。

git clone https://github.com/kenpos/StackBrowsingSystem

記事作成時のリビジョン:ff2bed9c98f1b228b5932795fbd27ae92d880769 はこちらです。

バックエンド(Go)

Github:labstack/echo このパッケージを使ってREST APIのような動きをするソフトを書きます。

go get github.com/labstack/echo

main.go

今回の肝となる部分はこの部分です。 前回の記事ではechoで文字列を返していましたが、今回は構造体を返すようにしています。 構造体の中身は、APIで取得されるパラメータ郡が格納されます。

func getEdinetAPI(c echo.Context) error {
//中略
    var edinet EdinetApiStruct
    if err := json.Unmarshal(body, &edinet); err != nil {
        fmt.Println(err)
    }
    return c.JSON(http.StatusOK, edinet)
}

c.JSON()を用いてJSONを返すことができます。

なお、構造体の定義はこのように作成しています。 EDINET APIから取得されるJSONをJSONtoGOというサービスに食わせて出力しています。

type EdinetApiStruct struct {
    Metadata struct {
        Title     string `json:"title"`
        Parameter struct {
            Date string `json:"date"`
            Type string `json:"type"`
        } `json:"parameter"`
        Resultset struct {
            Count int `json:"count"`
        } `json:"resultset"`
        ProcessDateTime string `json:"processDateTime"`
        Status          string `json:"status"`
        Message         string `json:"message"`
    } `json:"metadata"`
    Results []struct {
        SeqNumber            int         `json:"seqNumber"`
        DocID                string      `json:"docID"`
        EdinetCode           string      `json:"edinetCode"`
        SecCode              interface{} `json:"secCode"`
        JCN                  string      `json:"JCN"`
        FilerName            string      `json:"filerName"`
        FundCode             string      `json:"fundCode"`
        OrdinanceCode        string      `json:"ordinanceCode"`
        FormCode             string      `json:"formCode"`
        DocTypeCode          string      `json:"docTypeCode"`
        PeriodStart          string      `json:"periodStart"`
        PeriodEnd            string      `json:"periodEnd"`
        SubmitDateTime       string      `json:"submitDateTime"`
        DocDescription       string      `json:"docDescription"`
        IssuerEdinetCode     interface{} `json:"issuerEdinetCode"`
        SubjectEdinetCode    interface{} `json:"subjectEdinetCode"`
        SubsidiaryEdinetCode interface{} `json:"subsidiaryEdinetCode"`
        CurrentReportReason  interface{} `json:"currentReportReason"`
        ParentDocID          interface{} `json:"parentDocID"`
        OpeDateTime          interface{} `json:"opeDateTime"`
        WithdrawalStatus     string      `json:"withdrawalStatus"`
        DocInfoEditStatus    string      `json:"docInfoEditStatus"`
        DisclosureStatus     string      `json:"disclosureStatus"`
        XbrlFlag             string      `json:"xbrlFlag"`
        PdfFlag              string      `json:"pdfFlag"`
        AttachDocFlag        string      `json:"attachDocFlag"`
        EnglishDocFlag       string      `json:"englishDocFlag"`
    } `json:"results"`
}

フロントエンド(Vue)

ボタンを押したら、sendRequestEdinet関数を呼び出して取得した結果を表示します。 取得した値をv-forを用いて繰り返し呼び出します。 v-bind:keyは使っても使わなくとも表示上に影響はないのですが

公式からのアナウンスでもある通り、Vueが各ノードの識別情報の追跡を行うためには必要となります。 警告文もでますのでなるべくつけるようにしましょう。 https://vuejs.org/v2/guide/list.html#Maintaining-State

<template>
  <div class="hello">
    <button @click="sendRequestEdinet">有価証券報告書取得</button>
    <h1>取得結果</h1>
    <table id="metadata">
      <tr>
          <th>KEY</th>
          <th>DocID</th>
          <th>edinetCode</th>
          <th>filerName</th>
          <th>docDescription</th>
          <th>XbrlFlag</th>
          <th>PdfFlag</th>
      </tr>
      <tr v-for="(value, key) in edinet.results" v-bind:key="value.seqNumber">
          <td>{{ key }}</td>
          <td>{{ value.DocID }}</td>
          <td>{{ value.edinetCode }}</td>
          <td>{{ value.filerName }}</td>
          <td>{{ value.docDescription }}</td>
          <td>{{ value.XbrlFlag }}</td>
          <td>{{ value.PdfFlag }}</td>
      </tr>
  </table>
  </div>
</template>

実行結果

実行するにはvueプロジェクトをビルドして、main.goを立ち上げます。

yarn build
go run main.go

今回はGo側でポート番号を1323に設定しているので http://localhost:1323 にアクセスします。

アクセスしてボタンを押すとこのような画面がでます。 API取得結果はこのようになりました。

おわりに

この記事でまずは、WEB APIの結果を取得してVueに描画する方法がわかったと思います。 このあと有価証券報告書解析を進めようと思うと、pdfや特徴のあるXMLファイル取得とかが残っているので どこまでいけるかなというのもありますが…時間を見つけてやっていきたいですね。

前回記事の内容を少し詳細にしたような内容の記事です。

感じる所

子どもたちが小さいとまとまった時間取りにくいので更新頻度が落ちますね…

会社からはAWS勉強しておいてねって評価の目標に設定されたり… 英語早く人並みにしてねと熱いフォローが飛んできたり…

もっと真面目に勉強しておけばよかったなと思いますね。