SPF・DKIM・DMARCの簡単な解説

こんにちは岡野です。
時々分からなくなるためSPF・DKIM・DMARCについてまとめます。

前提知識

メールのFromアドレスには2種類ある。
エンベロープFrom:SMTP通信で使用するもの。メールヘッダ内ではReturn-Pathとして確認できる。
メールヘッダFrom:メール送信者が本文などとともに指定するもの。メールクライアントではこちらが表示されるため、なりすましを防ぐにはこちらが正しいことが重要。

SPF認証

エンベロープFromのドメインでDNSからIPアドレスを取得し、送信サーバのIPアドレスと一致することを確認する。

DKIM認証

メール署名時のドメインでDNSから公開鍵を取得し、ハッシュが一致することを確認する。

DMARC

SPF認証&SPFアライメント、もしくは、DKIM認証&DKIMアライメント、のいずれかが成り立つことを確認する。
SPFアライメント:エンベロープFromのドメインとメールヘッダFromのドメインが一致すること。
DKIMアライメント:DKIM署名のドメインとメールヘッダFromのドメインが一致すること。

結論

SPF・DKIM・DMARCすべて対応するのが望ましい。

初めてのkonva.jsインストール

こんにちは、陳です。
最近Konva.jsを使い始めたので、少しだけ共有したいと思います。


Konvaオフィシャルサイト
https://konvajs.org/index.html




Konva.jsとは何ですか?

Konva.jsは、ウェブ上で2Dグラフィックスを簡単に作成できるJavaScriptライブラリです。HTML5のcanvas要素の上に構築されており、図形、画像、テキストを描画したり、アニメーションやインタラクティブな機能を追加したりできます。シンプルな描画から複雑なアニメーションまで、Konva.jsはアイデアを実現するための強力で柔軟なツールセットを提供します。


Konva.jsのステージとレイヤーの概念

Konva.jsでは、ステージはキャンバス上のすべてを保持するメインのコンテナです。ステージは描画領域全体を表します。ステージの中には複数のレイヤーを持つことができます。各レイヤーは、図形、画像、テキストを描画できる透明な紙のシートのようなものです。レイヤーを使うことで、コンテンツを整理し、描画の異なる部分を独立して管理および更新することが容易になります。 (https://konvajs.org/docs/overview.html)


初めて行うのは、konva.jsのインストールです。
フロントエンドプログラムとしてVueやReactを考える場合、それに対応するvue-konvaやreact-konvaをインストールできます。それを使わずに、単純にkonva.jsをインストールすることも可能です。

ただ、インストールした際に、現在の環境でスムーズに動作するかどうかを考える必要があります。多少他のライブラリや設定を追加しないと動作しない場合があります。これらの判断や調整は少し面倒かもしれません。

今回、まだテスト段階ですが、指定したバージョンのkonva.min.jsをダウンロードして、このファイルをインポートする方法がかなり簡単です。既存の開発環境への影響が少なく、CDNリンクよりも安定しています。

Symfonyで運用する場合、使用したいTWIGページにダウンロードしたkonva.min.jsファイルをインポートし、通常のJavaScriptファイルと同様に、JavaScriptやVue、konvaを利用すればOKです。

ただし、vue-konvaを使用しないため、Vueのdataにパラメータを設定し、konvaのstage、layer、circleなどのコンポーネントの状態を保存、変更する必要があります。

例えば:
import Vue from 'vue'; new Vue({
     delimiters: ['${', '}'],
     el: '#editArea',
     mixins: [],
     data: {
             stage: null,
             layerCircle: null,
             circle: null,
           },
mounted() {
     this.showCircle();
          },
methods: {
     showCircle() {
         // 使用したい<div>を取る
         const container = this.$refs.container;
         const {width, height} = container.getBoundingClientRect();

         // stageを作成する
         this.stage = new Konva.Stage({
              container: container,
              width: width,
              height: height,
         });

         // layerを作成する
         this.layerCircle = new Konva.Layer();

         // 表示したいのサークルを設定する
         this.circle = new Konva.Circle({
              x: stage.width() / 2,
              y: stage.height() / 2,
              radius: 70,
              fill: 'yellow',
              draggable: true,
         });

         // layerをstageに追加する
         this.stage.add(this.layerCircle);
         // サークルをlayerに追加する
         this.layerCircle.add(this.circle);
         // 設定済みのlayerを描く(画面で表示する)
         this.layerCircle.draw();
                 },
         }
}) 


今回のシェアはここまでです。興味があれば、ぜひkonvaのサイトのDEMOを見てみてください。

チェックポイントファイルの拡張子について

くろはです。今日は画像生成AI関連でチェックポイント(モデルファイル)の拡張子について疑問に思って調べてみたことを共有したいと思います。 Googleなどで画像生成AIについて検索をするとおそらく目にするであろう「StableDiffusion」、このAIではチェックポイントという学習済データを指定することによって画風などをある程度決めることが可能です。そしてそのチェックポイントファイルは「Civitai」や「Hugging Face」といったウェブサイト上からダウンロードすることが可能です。 実際にCivitaiからチェックポイントファイルをDLしてみたところ、ファイルの拡張子が「.ckpt」と「.safetensors」というように複数あることに気づきました。今回はこの2種類の拡張子について軽くまとめます。

ckpt

「.ckpt」という拡張子は「pickle」というPythonモジュールを用いて直列化して保存されたデータに用いられる、Python固有のデータフォーマットです。pickleモジュールはPythonの各種オブジェクトをバイト列に変換したり、変換したりしたバイト列をファイルに保存したりする用途に使われます。 pickleの主な利点はPythonオブジェクトの状態を保存して後で再利用できることで、機械学習や大規模なデータ処理に非常に役立ちます。そんなpickleですがモジュールのトップレベルで定義されている組み込み関数などがPickle化できるため任意のコードが実行される可能性があり、脆弱性の問題を抱えています。この問題について、Pythonの公式ドキュメントでも注意喚起をしています。

safetensors

一方、「.safetensors」という拡張子は上で名前の出たHugging Faceによって開発された新しいシリアル化形式です。深層学習で重要なテンソルという呼ばれる大きくて複雑なデータの塊を保存、および取得するための特別な方法で、ファイルにはテンソルを安全に処理するためのアルゴリズムが保存されており悪意のあるコードに対して安全であると考えられています。 補足ですがテンソルとは、物理学やコンピューターサイエンスなどの様々な分野で使用される数学的な概念です。データを多次元配列として表現する方法です。ディープラーニングと人工知能の領域においてはテンソルはデータの編成と操作に使用される基本的なデータ構造として見られています。   以上、簡素ではありますが画像生成AIについて調べている過程で出会った2種類の拡張子についての共有でした。

scpコマンドでリモートからリモートにファイルを転送する

こんにちは。
今年はスマホで遊んでいたゲームが3本サービス終了して悲しいたにすぎです。
でも大丈夫、まだ舞台の予定がある……!
さて、3といえば最近SCPコマンドのオプションでリモートからリモートに転送できる方法があることに気づいたので共有します。

開発してるとたまに、テスト環境から○○のファイルをローカルに落として、ローカルから他のサーバにあげて動作確認するぞー……みたいな作業が発生して待ち時間とかちょっとだるかったりするのですが、なんとあるオプションを使うと1発で作業先のサーバまでファイルの転送が出来るんですなあ。 ■ 使い方
通常こんなふうに2回にわけて落として、上げてってやる必要がありますが。
scp user@src.example.com:~/sample.zip .
scp ./sample.zip user2@dist.example.com:~/sample.zip

なんと-3をつけるだけでこんな感じ1回で転送できます。
scp -3 user@src.example.com:~/sample.zip user2@dest.example.com
(portや鍵の指定は .ssh/config 辺りに接続設定書いておくと使う時にごちゃごちゃしなくてよい)

地味に便利だったので共有でした。へー(-3-)って思ってくれるとうれしいです。

高校の「ベクトル」に対して感じる違和感を見直してみる

こんにちは。こんばんば。イメージマジック三浦です。

Artificial Intelligence 略して「AI」という用語が一般に出るようになって、久しくなりました。この「AI」の裏側では「ベクトル空間」の世界の中で決められた規則に則って、大量の足し算と掛け算が実行されています。

私が「ベクトル」という言葉を初めて聞いたのは高校の数学(2022年度の学習指導要領では数学C)でした。当時「ベクトル」の定義は違和感を感じる表現で、そこから「ベクトル空間」へ飛び出すのはハードルが高かったと感じていました。

今回は高校の「ベクトル」の定義に対して感じた違和感を見直すことで、「ベクトル空間」への足掛かりになるような話を書いてみます。

ベクトルとは

高校の数学で出てくる「ベクトル」は、「大きさと向きを持つ量」という定義で出てきます(厳密な表現の違いはあるかもしれません)。
図では右のように矢印(有向線分)で表現します。
「大きさ」は点Aと点Bを結ぶ線の長さを測ればいいので、【量】と考えやすいです。

一方で「向き」はしっくりきません。右から左に向いているのは分かりますが、どうにも数値では表現しづらいです。 数値で表現できない概念を【量】と定義することに、高校生の時は違和感を持っていました。

「向き」を数値化する

「向き」を【量】とする違和感を「そういうもの」とやり過ごしていたら、大学の時に「ベクトル空間」を扱う線形代数学で苦労することになりました。この違和感を放っていたら、「ベクトル空間」へ飛び出す時の足枷になりそうです。
そこで「向き」を数値で表す方法を考えてみます。そのために、先ほどの矢印をXY座標平面に書き写してみます。話を簡単にするため、点Aは原点、点Bは整数値の座標で表現できたことにします(図1)。
右の図では、点Aから点Bへ移動するにはX軸方向に-3、Y軸方向に2移動すればよいです。 ここで、ベクトルのうち線分のみに注目して、原点から点Bまでの距離、X軸から線分ABまでの回転角が測れます。実際にやってみましょう。
図1
原点から点Bまでの距離:点A(原点)と点Bを結ぶ線分の長さとすれば、三平方の定理により√13≒3.606 と分かります。
X軸から線分ABまでの回転角:点Aを起点として、反時計周りに線分ABまで回転した角度を測ると、約146.31度という値がでます。
図2

言いたかったこと

XY座標平面を導入することで、「原点から点Bまでの距離」と「X軸から線分ABまでの回転角」が測れるようになりました。2つの測定値をベクトルの「大きさ」と「向き」とすれば、ベクトルが持つ2つの量を数値で表現できます。言い換えると、次の2つは同じ意味です。
  • XY座標の原点から点B(-3, 2)へ移動すること。
  • XY座標の原点を起点として、反時計周りに146.31°回転した方向へ、√13分の距離を移動すること。
    注意:原点を中心として回転するのは何週でもできますが、最終的に146.31°の位置で止まれば同じ方向です。ただし話を簡単にするため、回転角は0°~360°の間に限定して考えることを前提にします。
これで、高校当時抱いていた「向きは数値で表しづらい」という違和感に1つ答えを出せました。

ちなみに、単にベクトルというだけでは位置は関係しませんが、片方の点を固定することでもう一方の点が決まります。そうすると、ベクトルが固定した点からの位置を表現できるようになり、「位置ベクトル」という考え方が出てきます。詳しい定義は、教科書や解説サイトに譲ります。

次の話

ベクトルをXY座標平面に置くことで、ベクトルの「大きさ」と「向き」を数値で表せること、XY座標表現に置き換えられることが分かりました。ここでXY座標表現を使うと、ベクトルの「計算」を数値の計算に持ち込みやすくなります。次回はベクトルの「計算」について触れてみます。

OS Loginを有効にしたGCEにPuttyやWinSCPで接続する方法

こんにちは岡野です。
GCPのCompute EngineへPuttyやWinSCPで接続したいという要望があり検索したのですが、なかなか情報が無かったためここで共有します。

・GCEの状態

外部IPアドレス未設定
OS Login有効

・接続方法

SSHトンネルを利用する。

1. 以下のコマンドをWSL上などで実行。

gcloud compute ssh {{ gce name }} --ssh-flag="-L 10001:localhost:22 -N -f"
    1. PuttyやWinSCPなどでは以下の情報で接続する。
    ユーザ名:GCPで使用しているアカウント(通常はメアドの記号を_へ置換したもの、例:name_example_com)
    ホスト名:localhost
    ポート:10001
    秘密鍵:GCP用の鍵

    ImageMagickでのPNGファイル生成の高速化

     
    こんにちは岡野です。
    弊社サービスではImageMagickでPNGファイルを生成することが多いのですが、高速化するための設定を調査したので共有します。
     

    環境

    ImageMagick 6.9.12-84 Q8
    Ubuntu 22.04
     

    調査内容

    PNGファイル出力時の圧縮設定を変更した際の出力時間を調査しました。なおこの解説記事を参考にしています。

    圧縮方法を設定する-quality引数を0~99で変えながら実行した際の時間を数回計測しました。

    使用したコマンドは
    $ convert logo: -resize 1024×1024! logo.png
    $ rm log ; for q in `seq 0 1 99` ; do /usr/bin/time -f %e:$q -a -o log convert logo.png -quality $q png32:logo-$q.png ; done
    $ sort log

    で、結果は以下です。
    11,12,13,14,21,22,23,24,31,32,33,34は0.05秒
    41,42,43,44,51,52,54は0.06秒
    以降、省略

    ファイルサイズは以下の通りでした。
    $ ls -l logo-[123][1234].png logo-75.png
    384337  3月 24 17:31 logo-11.png
    384337  3月 24 17:31 logo-12.png
    384337  3月 24 17:31 logo-13.png
    384337  3月 24 17:31 logo-14.png
    378112  3月 24 17:31 logo-21.png
    378112  3月 24 17:31 logo-22.png
    378112  3月 24 17:31 logo-23.png
    378112  3月 24 17:31 logo-24.png
    374669  3月 24 17:31 logo-31.png
    374669  3月 24 17:31 logo-32.png
    374669  3月 24 17:31 logo-33.png
    374669  3月 24 17:31 logo-34.png
    295753  3月 24 21:00 logo-75.png (デフォルト設定)

    11,12,13,14,21,22,23,24,31,32,33,34のいずれかの設定が良さそうでした。ただしpng:compression-filterの仕様を見ると画像内容によって上記の結果も結構変わりそうです。
     

    結論

    今後も検討を続けますが一旦は-quality 11を使用することにしました。ただしファイルサイズがデフォルト設定より増えるため(295,753->384,337)、ユーザアクセス内容によっては弊害も大きく注意が必要です。
     

    データベースインデックスの作成に関する感想

    あけましておめでとうございます。陳です。
    今日は、データベースのインデックスについてお話ししたいと思います。ご存じの通り、SQLインデックスはクエリの検索パフォーマンスを向上させ、データベースの効率を高めるためのものです。


    インデックス作成

    例:CREATE INDEX idx_name ON Student (name);

    複合カラムインデックスもよく使われています。


    例:CREATE INDEX idx_name_class ON Student (name, class);


    インデックス削除

    例:DROP INDEX idx_name ON Student; Indexを作成したら、EXPLAIN でindexの使用状況を確認できます。
    実行したら、以下のような結果が得られます。
    EXPLAIN SELECT * FROM Student WHERE name = 'John';

    +----+-------------+---------+------------+------+---------------+----------+-----+------+------+----------+----------------+
    | id | select_type | table | partitions | type | possible_keys | key   | key_len | ref | rows | filtered | Extra |
    +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
    | 1 | SIMPLE | Student | NULL | ref | idx_name | idx_name | 767 | const| 1 | 100.00 | Using index|
    +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+


    説明:

    - id: クエリの選択部分の識別子。外部のクエリの場合は1です。
    - select_type: SELECTのタイプ。この場合はSIMPLEで、サブクエリやUNIONのない単純なSELECT。
    - table: アクセスされているテーブル。
    - type: 使用されているアクセス方法のタイプ(例:フルテーブルスキャンの場合はALL)。
    - possible_keys: 使用可能なインデックス。
    - key: オプティマイザによって実際に選択されたインデックス。
    - key_len: 使用されたキーの長さ。
    - ref: インデックス名で指定された列または定数。
    - rows: クエリを実行するためにMySQLが調査する行の推定数。
    - filtered: テーブル条件によってフィルタリングされた行の割合。
    - Extra: クエリの実行に関する追加情報。
     
    初めてのindexなら、私はまずpossible_keysにこのindexが考えられるかどうかを確認します。次に、実際に選択されたindexとクエリを実行する情報を確認します。
     
    最も重要なのは rows 列です。なぜなら、これはMySQLがクエリに回答するために処理する必要がある行の数を示しています。extra の情報もクエリの実行方法をより理解しやすくします。例えば、extra に ‘using index condition; using where;‘ と書かれている場合、インデックスを使用した後に結果をフィルタリングする必要があったことを示します。
     
    複数の複合カラムインデックスを作成して、運用する時、考え通りに動かないことがありますね。explainを実行すると、possible_keysにはあるのに、使われていませんといったケースが発生することがあります。また、使われていても、クエリの効率が上がらないこともあります。
     
    それは、複合カラムインデックスに設定されたカラムの順序のためかもしれません。
     
    この順序と実行されたクエリ条件の順番が異なる場合、使われてもらっていない状況があります。そのため、複合カラムインデックスを作成する際は、順序が重要です。頻繁にクエリの条件として使用される列が左側にあるほど良いです。
     
    また、列の一意性が高いほど、より多くの値をフィルタリングでき、検索の速度が向上します。列の分散性も考慮する必要があります。分散性は、各値の数量の差異を示します。
     
    迷った時、Index Hints というものを使ってクエリにするINDEXを指定して違うIndexを使用してexplainで結果を考えることも便利と思います。
     
    USE INDEX はindexを使用することを提案しますが、必ずしも指定通りには使用されません。FORCE INDEX は指定したINDEXのみを使用します。
    例:
    SELECT *
    FROM Student USE INDEX (idx_age)
    WHERE age > 20;
     
    今回の感想は以上です。EXPLAIN に興味がある方は、以下の記事での説明が詳しく、理解しやすいので、お勧めさせていただきます。

    Using EXPLAIN in MySQL to analyze and improve query performance:
    https://medium.com/datadenys/using-explain-in-mysql-to-analyze-and-improve-query-performance-f58357deb2aa

    PHPのstdClassの動きが環境によって違った件

    スズキです。PHPのstdClassのプロパティにアクセスしようとしたときの挙動が、環境によって異なる現象に遭遇したので、共有です。

    やりたかったこと

    • json文字列の中から「orderDate」というプロパティから日付データを取り出したい
    • 「orderDate」というプロパティが存在しない場合は、代わりに今日の日付を使いたい
    json文字列のイメージはこんな感じ。
    {
        "orderId": 1001,
        "orderDate": "2023-11-30",
        "itemName": "UVコーティングシール"
    }

    修正前コード(問題あり)

    PHPのjson_decodeでjson文字列からstdClassを作って、jsonの中身を使うプログラムです。
    // $jsonObjectは「stdClass」クラス
    $jsonObject = json_decode($jsonString, false);
    try {
        $prop = $jsonObject->orderDate;  // ①orderDateがないときはExceptionが発生する…はず?
        $orderDate = new DateTime($prop);  // ②$propがnullだとTypeErrorが発生
    } catch (Exception $e) {
        $orderDate = new DateTime();  // ③今日の日付を使う
    }
    ①の行にて「orderDate」というプロパティがないとき、Exceptionが発生してくれれば、catchブロックの③にジャンプして今日の日付を$orderDateにセットできる、ということを目論んだコードなのですけど、これがダメでした。 自分の開発環境では①にてErrorExceptionが発生したので、期待通りcatchブロックへジャンプしたのですが、別のサーバに乗せたら①にてErrorExceptionは発生せず、nullが取得されたのです。nullが取得されたことで、その後の②でTypeErrorが発生したのですが、TypeErrorはExceptionを継承していないのでcatchできず、結局処理としてはエラーになってしまったというわけです。 つまり、json_decodeで取得した「stdClass」の挙動が、環境によって異なったのですね。個人的には、 $jsonObject->orderDate; のところで、プロパティが無ければnullが取れるのが直感的で好きです。

    修正後コード(問題解消)

    「stdClass」クラスの挙動の違いはさておき、そもそもjsonのプロパティがないときの処理を例外処理を利用して実現しようとしているのが圧倒的によくないので、普通にif文で書き直しました。なんで例外処理でやろうと思ったのだろう、当時の私…
    // $jsonObjectは「stdClass」クラス
    $jsonObject = json_decode($jsonString, false);
    if (property_exists($jsonObject, 'orderDate') && !empty($jsonObject->orderDate)) {
        $prop = $jsonObject->orderDate;
        $orderDate = new DateTime($prop);
    } else {
        $orderDate = new DateTime();
    }

    まとめ

    ということで、「stdClass」クラスに存在しないプロパティにアロー演算子(->)でアクセスしたときの挙動は環境によって異なり、ErrorExceptionが発生したり、nullが取得できたりする、というメモでした。PHPのバージョン違いかも?と思いきや、確認したら同じバージョン番号だったので謎は深まるばかり。 どういう環境だと挙動が異なるのか、についての調査は難航しそうだったので、今回はこのへんで…

    JsonSchemaについて

    こんにちは、黒羽です。 ここしばらく開発しているアプリケーションでは、GUI画面の入力項目やブラウザからWebSocketのメッセージとして渡されたパラメータをJsonSchemaファイルに定義した条件でバリデーションするという実装を行ったので定義ファイルを作成するときに詰まったポイントなどを共有します。   そもそもJsonSchemaってなんやねんってときはググればだいたい解決できる世の中ですが、ものすごくざっくりというとプロパティの検証に必要な条件をJson形式で記述したスキーマ言語です。 以下のJsonはかの有名な「見た目は子供、頭脳は大人」な某小学生のプロフィールを基に書いてみました。
    {
        "name": "江戸川コナン",
        "age": 6,
        "birthdayMonth": "May",
        "birthdayDay": 4,
        "sex": "male",
        "job": ["student",
            "detective"
        ]
    }
    この情報をベースに某小学生の通う小学校に入ることのできる人物を定義してみました。
    {
        "$schema": "https://json-schema.org/draft/2019-09/schema",
        "$id": "http://example.com/example.json",
        "type": "object",
        "default": {},
        "required": [
            "name",
            "age",
            "birthdayMonth",
            "birthdayDay",
            "sex",
            "job"
        ],
        "properties": {
            "name": {
                "type": "string",
                "minLength": 1,
            },
            "age": {
                "type": "integer",
                "default": 0,
                "minValue": 5
            },
            "birthdayMonth": {
                "type": "string",
                "enum": [ "Jan", "Feb", "Mar", "Apr", "May","Jun", "Jul", "Aug"
                , "Sep", "Oct", "Nov", "Dec"
                ]
            },
            "birthdayDay": {
                "type": "integer",
                "default": 1,
                "maxValue": 31,
    "minValue": 1 }, "sex": { "type": "string", "default": "", "enum": [ "male","female" ] }, "job": { "type": "array", "default": [], "items": { "type": "string", "enum": [ "student", "detective", "parent" ] } } } }
    定義した条件としては
    • 名前を1文字以上持っていること
    • 5歳以上であること
    • 誕生月の略称がenumで定義した項目に含まれること
    • 誕生日が1~31の範囲であること
    • 性別が男女のどちらかであること
    • 職業が学生、探偵、保護者のいずれかであること
    となります。 主題から脱線しそうなのでこれ以上の深掘りはしませんが某黒ずくめの組織の長髪の兄貴だとか警察関係者は立ち入り禁止、眠りの名探偵は職業が探偵、保護者なので立ち入り許可が出ます。 基本的なデータ型のチェック(type)、数字型の値の範囲の判定(minValue, maxValue)、リスト内の項目合致(enum)と基本的なものであれば上記の定義だけで十分です。 if分岐なども可能なので条件によって子プロパティが動的に変わるプロパティなども定義はできますがかなり記述量が増えます。 ここからは不便な点、詰まった点を共有します。

    判定の結果がエラーだった場合において、エラー理由を取得すると日本語以外のメッセージしか取得できない

    現状、JsonSchemaのエラーメッセージは日本語に対応しておらず、英語含めて3か国語程度しか選択できないようです。 日本国内での使用が一番想定されている開発中のアプリではユーザーに英語のエラーメッセージを解読してもらうのは忍びないのでJsonSchemaに用意されている予約語以外のフィールド名を使用するとAnnotationsというフィールドにオブジェクトとしてまとめられる仕様を利用して日本語メッセージを返すようにしています。

    動的なプロパティを作ろうとすると記述量が膨大になる

    if分岐があると上で書きましたがif-thenをJson形式で書くため、素直に書くと記述量がとんでもないことになります。 今回のアプリではcase文のような分岐が必要だったため以下のような雰囲気で記述しました。
    "allOf": [
            {
                "if": {
                    "properties": {
                        "job": {"const": "student"}
                    }
                },
                "then": {
                    "properties": {
                        "details": { "$ref": "#/definitions/student" }
                    }
                }
            },
            {
                "if": {
                    "properties": {
                        "job": { "const": "detective" }
                    }
                },
                "then": {
                    "properties": {
                       "details": { "$ref": "#/definitions/detective" }
                    }
                }
            }
        ]
    見慣れないプロパティはドキュメント等を参照してもらうとして、条件としては「jobが”student”だった場合はdefinitionsプロパティに定義したstudentのdetailsプロパティを参照し、jobが”detective”だった場合はdetectiveのdetailsプロパティを参照する、それ以外はdetailsを持たない」となっています。 allOfのおかげでここは比較的に簡潔に書けました。(それでもdefinitions項目に別途色々定義したりする必要があるのでJson自体はカオス。)

    Formatのチェックがあるがかなり甘い

    文字列が特定の形式にのっとっているかを判定してくれるFormatフィールドがあり、URIやIPアドレスの形式チェックができます。これは便利!!!と思って使っていますが細かいところでNGパターンがチェックからすり抜けます。止む無し。 試した過程であったのは
    • IPアドレス→第4オクテットがなくても正常と判断される
    • URI→「:/」でも正常と判断される
    といったものです。他のサイトを調べても「Formatだけじゃ不十分だからPatternできちんと正規表現で潰してね!」みたいな感じだったのでFormatとPatternの併用で潰している箇所があります。(ポート番号はさすがにないけど最大値最小値で判定するのは微妙だったのでここも正規表現を使って潰していたり。)  

    終わりに

    今回はJsonSchemaについてでした。 入力値の簡単なチェック処理をソースコードにだらっと書くのが嫌い、美しくない!と思う方は試してみると面白いかもしれないです。