Movatterモバイル変換


[0]ホーム

URL:


えいのうにっき

a-knowの日記です

中身が不定のJSONオブジェクトをGo言語で扱うのに mattn/go-jsonpointer が便利だった

今日書くのは、先日Go言語の個人プロジェクトである Pixela に手を加えた際に実感したことについて。

先日手を加えたものの一部に、以下のようなものがあった。

> 以下のようなコマンドを実行してみましょう。>> curl -X GET https://pixe.la/v1/users/a-know/graphs/stopwatch-test/20200504 -H 'X-USER-TOKEN:thisissecret'>> Pixela では、各日付ごとのデータを pixel と呼んでいるのですが、その詳細を取得できるコマンドです。`20200504` のところは詳細を確認したい日付を指定します。このコマンドの実行がうまくいけば、以下のような結果が表示されると思います。>> {"quantity":"0.50","optionalData":"{\"stopwatchUsage\":{\"stopwatchUseCount\":2,\"stopwatchPeriods\":[\"20:04:48 - 20:05:00\",\"20:05:13 - 20:05:30\"]}}"}>> quantity というのはその日の計測時間の合計値のことです。一方、optionalData に指定されているのは JSON 形式で表されたオブジェクトで、stopwatchUseCount には「計測開始&終了を何セット実施したか」というカウントが、stopwatchPeriods には「計測対象となった時間帯」が、それぞれ記録されています。上記の例だと、「計測開始&終了を2セット」「計測したのは 20時04分48秒〜20時05分00秒 と 20時05分13〜20時05分30秒」という情報がセットされている形になります。

Pixel という以下のような(とはいえ、説明に不要なものは省略している。)エンティティがあって、

type Pixelstruct {    Datestring`json:"date"`    Quantitystring`json:"quantity"`    OptionalDatastring`json:"optionalData"`}

このうちOptionalData は、ユーザーが自由にJSON文字列を格納することができる、という仕様。そんな仕様だったところに、今回のアップデートで、システム側もOptionalData を用いるようにしたくなった、ということになる。

ざっくり要件をまとめるとすると、以下のような感じだろうか。

  • OptionalData を Unmarshal したものに、"stopwatchUsage":{"stopwatchUseCount":1,"stopwatchPeriods":["20:00:00 - 20:05:00"]} といったオブジェクトを突っ込みたい。
  • OptionalData にもともと格納されていた情報は、もちろん維持しなければならない。
    • どんな情報が格納されているかはわからない。保証されていることは、それがJSON形式の文字列である、ということだけ。
    • キーがstopwatchUsage と重複するケースは考慮しない(システム側が上書きすることを許容する)

最初に行き着いた情報はこちらgolang は ゆるふわに JSON を扱えまぁす! — KaoriYa 。ただ、ここにある方法だと、全くの未知の構造のオブジェクトに対して適用することができないように思われた(これはこれで便利に使える場面はあるものだと感じたけれど)。

ただ、上記のページ内で紹介されていたmattn/go-jsonpointer が、まさに今回のケースでぴったりハマりそうなもので、実際、これを使うことで非常にすんなり実装を終えることができた。

github.com

JSON Pointer の GO実装。以下に、今回のケースにおいてこれを用いた実例を示す。

以下のように、OptionalData に突っ込みたい struct を予め用意しておき、

count :=1periods := []string{"20:00:00 - 20:05:00"}usage := &stopwatchUsage{    StopwatchUseCount: count,    StopwatchPeriods:  periods,}

*interface{} を渡してOptionalData を Unmarshal。

var objinterface{}json.Unmarshal([]byte(pixel.OptionalData), &obj)

あとは、Unmarshal されたものに対して go-jsonpointer を使って、stopwatchUsage というキーで set してやるだけでよかった。

jsonpointer.Set(obj, "/stopwatchUsage", usage)optinalDataString, err := json.Marshal(obj)

/stopwatchUsage というのがJSON Pointer(RFC6901 として標準化された、JSONオブジェクトに対するクエリ文字列)。

他にも、GetRemove ができる。リポジトリの README に非常に簡潔に書いていただいていたので、迷うことも何もなかった。「OptionalData 、なんでそんな自由な項目にしたんや......ワイのバカ!」なんて後悔することもなくて、本当に良かった(反省はしたほうがよさそう)。感謝です。

関連

PVs this Blog
書いてる人
id:a-knowid:a-knowはてなブログPro

"カスタマー x エンジニアリング" なお仕事に取り組み始めて、はやn年。今は SmartHR でソフトウェアエンジニアをしています。岡山県倉敷市でフルリモートワーク。 個人プロジェクトとして Pixelahttps://pixe.la/ , Okayama.なんかhttps://okayama-nanka.org/

カテゴリー

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp