JSONを整形するツール

本当はゴルーチンについて書きたいんだけど、まだ頭の中で整理ができてないので今日は別のことを書く。

Web サービスやなんかの API で JSON を返してくれるのはよくあること。ただ、基本的にプログラムが処理するように想定されていて、人間が見やすい形にはなっていないことが多い。まあ、当たり前ではあるんだけど、それでも目で見て確かめたいときもある。そういう時は見やすく整形してくれるツールがほしくなる。
ググると jq っていうツールが見つかる。このツール自体は、JSON の整形だけでなく検索とかいろいろできるようで、おまけに Windows 用のバイナリもあるのでちょっと使ってみた。
ところが!もとの JSON が UTF-8 のせいかもしれないけど、Windows のコマンドプロンプトでは日本語が文字化けしてしまって読めない。おまけにどういうわけか、コマンドプロンプトのフォントが変更されてしまうという、謎の現象に見舞われた。
これでは使えないので、じゃあ、Go で整形するだけのツールを書いてみようか、と思って書いたら意外にも簡単だった、というのが今日の話。

サンプルの JSON はこんなの。Web で拾ったサンプルだけど、どこのページだか忘れてしまった。

{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "pref": "富山県", "city1": "下新川郡", "city2": "朝日町" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 136.111111, 36.111111 ], [ 136.222222, 36.222222 ] ] ] } }, { "type": "Feature", "properties": { "pref": "富山県", "city1": "氷見市", "city2": "" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 136.333333, 36.333333 ], [ 136.444444, 36.444444 ] ] ] } }, { "type": "Feature", "properties": { "pref": "富山県", "city1": "高岡市", "city2": "" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 136.555555, 36.555555 ], [ 138.666666, 36.666666 ] ] ] } } ]}

全部が1行に詰め込まれてる上に、日本語が混じっている。

で、書いたツールがこれ。pj という名前にした(ファイル名は main.go だけど)。整形には encoding/json パッケージの Indent 関数を使っている。

package main

import (
    "fmt"
    "encoding/json"
    "bytes"
    "os"
    "io/ioutil"
    "flag"
)

const (
    progVersion = "v0.1.0"
)

func prettyJson(src []byte) string {
    buf := make([]byte, 0)
    dst := bytes.NewBuffer(buf)
    err := json.Indent(dst, src, "", " ")
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    return dst.String()
}

func main() {
    flag.Usage = func() {
    fmt.Fprintf(os.Stderr,
    `Usage:
%s [options] 
Options:
`, os.Args[0])
    flag.PrintDefaults()
}
    opt_version := flag.Bool("version", false, "Show version.")
    flag.Parse()

    if *opt_version {
        fmt.Println(progVersion)
        os.Exit(0)
    }

    infile := flag.Args()[0]
    src, err := ioutil.ReadFile(infile)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    pretty := prettyJson(src)
    fmt.Println(pretty)
}

試してみよう。

^o^ > go build

^o^ > pj sample.json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "pref": "富山県",
        "city1": "下新川郡",
        "city2": "朝日町"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              136.111111,
              36.111111
            ],
            [
              136.222222,
              36.222222
            ]
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "pref": "富山県",
        "city1": "氷見市",
        "city2": ""
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              136.333333,
              36.333333
            ],
            [
              136.444444,
              36.444444
            ]
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "pref": "富山県",
        "city1": "高岡市",
        "city2": ""
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              136.555555,
              36.555555
            ],
            [
              138.666666,
              36.666666
            ]
          ]
        ]
      }
    }
  ]
}

何のデータなんだかよくわからないけど、うまくいった。もとのエンコーディングは UTF-8 だけど、Windows のコマンドプロンプトに出力しても文字化けしない。もちろんファイルに書き出せばちゃんと UTF-8 で保存される。
うまくいったので GitHub にも公開しておいた。

cf. https://github.com/takatoh/pj

インストールするにはこうすればいい:

^o^ > go get github.com/takatoh/pj