mdconf でドキュメントから JSON Schema を生成する


※ 「JSON Schema からドキュメントを生成する」ではなく,「ドキュメントから JSON Schema を生成する」です.

はじめに

SDD (Specification Driven Development) では,Swagger,RAML といった API 定義フォーマットや,JSON Schema といった JSON Object 定義フォーマットなどがよく利用されます.これらのフォーマットからコードジェネレートしつつ,ドキュメントも生成することで,仕様と実装の乖離がなくなるといったメリットがあります.

よく見かけるのは,JSON (YAML) をソースとして管理し,それから HTML ドキュメントを生成するようなものですが,エンジニア以外のステークホルダがいる環境で運用するにあたっては,HTML のホスティングサーバを用意したり,それをアップデートし続けるジョブを用意したりと少々面倒なこともあります.

そこで今回は,JSON からドキュメントを生成するのではなく,ドキュメントから JSON を生成する ことで,上記の解決を試みます.ドキュメントはマークダウンで記述します.GitHub などは,マークダウンを HTML に変換して表示してくれるので,記述したマークダウンそのものがドキュメントとなります.つまり,ドキュメントが生成物ではなくソースになるため,GitHub などでソース管理するだけで,常に最新のドキュメントを閲覧することができます.

mdconf を使って JSON にパースする

例として,以下のような JSON Schema を利用します.

user.json
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "id": "user",
  "type": "object",
  "additionalproperties": "false",
  "required": [
    "id",
    "lastName",
    "firstName",
    "state"
  ],
  "properties": {
    "id": {
      "type": "number"
    },
    "lastname": {
      "type": "string"
    },
    "firstname": {
      "type": "string"
    },
    "state": {
      "type": "number",
      "enum": [
        "1",
        "2"
      ]
    }
  }
}

マークダウンをパースして JSON にするために, mdconf を使います.mdconf では,#- を用いてプロパティや値を表現します.これらのいずれも付いていない行はパースの対象外なので,自由に記述することができます.

以下は,上記の JSON Schema を生成するマークダウンを記述したものになります.

user.md
- $schema: http://json-schema.org/draft-04/schema#

- id: user

- type: object

- additionalProperties: false

# required

- id

- lastName

- firstName

- state

# properties

## id

user id

- type: number

## lastName

user's last name

- type: string

## firstName

user's first name

- type: string

## state

user state

- type: number

### enum

active

- 1

inactive

- 2

GitHub では,以下のように表示されます.スキーマ定義が見やすいドキュメントになっていますね.このドキュメントをメンテナンスしていくことになります.

以下が,このマークダウンをコードで利用し,バリデーションをおこなう例です.mdconf により簡単にパースすることができます.

import fs from 'fs'
import path from 'path'
import isMyJsonValid from 'is-my-json-valid'
import parse from 'mdconf'
import assert from 'assert'

// マークダウンを読み込み, JSON にパース
const mdFilePath = path.resolve(__dirname, 'user.md')
const schema = parse(fs.readFileSync(mdFilePath, 'utf8').toString()))

const userData = { id: 1, lastName: 'Yamada', firstName: 'Taro', state: 1 }

// データが想定しているフォーマットかどうか検証
const validator = isMyJsonValid(schema)
assert(validator(userData), validator.errors)

おわりに

マークダウンで Schema 定義を記述することで,ドキュメントとして利用しつつ,かつコードからも直接利用することができるようになりました.少々アグレッシブではありますが,Swagger や RAML などでも汎用的に使える方法ですので,興味を持たれた方はぜひ試してみてください.