Go言語でゆるふわにYAMLを使う


はじめに

YAMLをGo言語でパースしたり、逆に文字列に戻したりしたかったのですが、struct定義をせずにやる方法をまとめた記事が見当たらなかったので、忘れない内に記事にします。

サンプルのソースコードは以下に置きました。
https://github.com/segurvita/yaml-encoding-practice

用意したYAML

以前の記事で作成したswagger.yamlを再利用します。

swagger: '2.0'
info:
  description: これはアパートに関するAPIです。
  version: 0.0.1
  title: アパートAPI
paths:
  '/rooms/{room-id}':
    get:
      summary: 部屋情報API
      description: 指定されたroom-idの情報を返します
      parameters:
        - name: room-id
          in: path
          description: 取得したい部屋のID
          required: true
          type: integer
          format: int64
      responses:
        '200':
          description: OK
          schema:
            type: object
            properties:
              id:
                type: integer
                format: int64
                example: 404
              comment:
                type: string
                example: 404号室です。どこにも存在しない部屋かも。

サンプルコード

main.goの中身はこんな感じです。YAML文字列をオブジェクトに変換して、そのオブジェクトをYAML文字列に戻すということをしています。(意味はあまりありません。)

package main

import "fmt"
import "gopkg.in/yaml.v2"

func main() {
    // 入力YAMLを定義
    yamlInput := []byte(`
swagger: '2.0'
info:
  description: これはアパートに関するAPIです。
  version: 0.0.1
  title: アパートAPI
paths:
  '/rooms/{room-id}':
    get:
      summary: 部屋情報API
      description: 指定されたroom-idの情報を返します
      parameters:
        - name: room-id
          in: path
          description: 取得したい部屋のID
          required: true
          type: integer
          format: int64
      responses:
        '200':
          description: OK
          schema:
            type: object
            properties:
              id:
                type: integer
                format: int64
                example: 404
              comment:
                type: string
                example: 404号室です。どこにも存在しない部屋かも。
  `)

    // YAMLをオブジェクトに変換
    var objInput interface{}
    err := yaml.Unmarshal(yamlInput, &objInput)
    if err != nil {
        fmt.Println("エラー: ", err)
    }

    // オブジェクトをYAMLに変換
    yamlOutput, err := yaml.Marshal(&objInput)
    if err != nil {
        fmt.Println("エラー: ", err)
    }

    // 標準出力
    fmt.Println("# ------------------------------------------------------------")
    fmt.Println("# 入力YAML:")
    fmt.Println("# ------------------------------------------------------------")
    fmt.Println(string(yamlInput), "\n")
    fmt.Println("# ------------------------------------------------------------")
    fmt.Println("# オブジェクト:")
    fmt.Println("# ------------------------------------------------------------")
    fmt.Println(objInput, "\n")
    fmt.Println("# ------------------------------------------------------------")
    fmt.Println("# 出力YAML:")
    fmt.Println("# ------------------------------------------------------------")
    fmt.Println(string(yamlOutput), "\n")
}

解説

YAMLの解析処理にはgo-yamlを使いました。

  • Unmarshalがparse処理です。文字列をオブジェクトに変換します。
  • Marshal がstringify処理です。オブジェクトを文字列に変換します。

Unmarshalの第二引数にinterface{}な変数を指定することで、struct定義を回避しています。(便利!)

Marshal も特に struct を定義せずに使用できました。

出力結果

上記のコードを実行すると、以下のように出力されました。

info:
  description: これはアパートに関するAPIです。
  title: アパートAPI
  version: 0.0.1
paths:
  /rooms/{room-id}:
    get:
      description: 指定されたroom-idの情報を返します
      parameters:
      - description: 取得したい部屋のID
        format: int64
        in: path
        name: room-id
        required: true
        type: integer
      responses:
        "200":
          description: OK
          schema:
            properties:
              comment:
                example: 404号室です。どこにも存在しない部屋かも。
                type: string
              id:
                example: 404
                format: int64
                type: integer
            type: object
      summary: 部屋情報API
swagger: "2.0"

同じ階層のキーがアルファベット順でソートされてしまいましたが、内容は入力したYAMLとまったく同一でした。

参考サイト

以下のサイトを参考にさせていただきました。

さいごに

意外と簡単にできて良かったです。