CSV Schemaを使ったCSVのお手軽入力チェック

# CSV Schemaとは

* 簡単に言うとCSVデータの定義とValidationを目的としたDSLです
  * ほぼJSON SchemaのCSV版といってよさそうです
* 仕様は[こちら](http://digital-preservation.github.io/csv-schema/csv-schema-1.0.html)をどうぞ

# CSVこれまで

* CSVは[RFC4180 - 日本訳](http://www.kasai.fm/wiki/rfc4180jp)で標準化されてますが、策定が遅かったため各ベンダー間でデータ形式がバラバラです
* それによって特に困る領域の一つにEDI（データ交換）があります
なにせ各システムが示すCSVが実際どのようなフォーマットなのか、認識を合わせするところから始まる必要があります
  - （例）区切り文字が「カンマ」or「タブ」 か
  - （例）囲み文字が「ダブルクォート」or「シングルクウォート」or「無し」 か
  - （例）エスケープの有無
  - （例）ヘッダ行の有無
  - （例）項目中の改行や区切り文字はどういう仕様で扱うか
  - etc.
* これがクリア出来た後に、実際のデータ項目のがどのような意味か（IDとか金額とか）の話をすることが出来ます



# 項目チェック

* ある項目がユーザIDを指し、ユーザIDは8桁で半角英数字のみで構成されますという約束でデータ連携した時、これの入力チェックは非常に簡単なように思われます。なにせ約束したのですから。
* ただし実際は **世 の 中 甘 く 出 来 て い な い** です

* まず桁数が6桁で連携してきます。担当者に連絡すると、あろうことかそれが正しい仕様だと言ってくるんです。
8桁と言っていたのに、です。6桁をどうしろって？　こちらのシステムで左から0埋めしてくれと言うんです。
初耳ですよその仕様！？最初から言ってよ！

* 半角英数字？　当然のように記号が入ってきます。ハイフンやアンダースコアです。これは英数字なんですかね？
半角英数字って```[0-9a-zA-Z]```って定義しましたよね？

* また記号が入ってました。おお、今度は半角スペースですか。なるほど、非エンジニアであればあまり意識しないかもしれないね！正規表現まで提示したのにガン無視ですね！

*　今回は非常にシンプルなケースでしたが、連携データが「売掛金」など金銭を扱うケースだとより神経質にチェックを行わざる負えず、非常に苦労が耐えません。
あー、もっと簡単にデータチェックしたいのにー
　

# というわけでCSV Schemaです

* CSV SchemaはCSVデータの構造定義を表現する標準的な仕様です
* 今までのCSV定義は各々が勝手に決めて作ったり、必ずしもプログラムからの読み取りに適したフォーマットではなかったので、定義が標準化されるのは非常に嬉しいことなのです
  * これでJSON-Schemaを羨ましがらなくてすみます！

* 更にCSV-Schemaという標準化された定義から、各言語技術ごとにValidationツールが出てくると思いますので、これをお互い利用することでちょっとした認識の違いを早期に発見できます。良かったよかった。


# 実装

すでにCSV SchemaによるValidationを実装されていたモジュールがあったので試してみます。

CSV Validator
A Validation Tool and APIs for validating CSV (Comma Separated Value) files by using CSV Schema.
https://github.com/digital-preservation/csv-validator

## 条件

入力のCSVです。
色々データが不正です。

```csv:入力データ（ivalid.csv）
name,age,gender
james,4 years,m
lauren,19,f
simon,57,malename,age,gender
james,21,m
lauren,19,f
simon,57,m
```

CSVスキーマです。
CSVスキーマなのにCSVで表現しないんだってツッコミは無しですよ。
拡張子は.csvsみたいです。

```csv:CSVスキーマ（example.csvs）
version 1.0
@totalColumns 3
name: notEmpty
age: range(0, 120)
gender: is("m") or is("f") or is("t") or is("n") 
```

依存ライブラリです

```xml:pom.xml
<dependency>
    <groupId>uk.gov.nationalarchives</groupId>
    <artifactId>csv-validator-java-api</artifactId>
    <version>1.0</version>
</dependency>
```


実行コードです。
importは```uk.gov.nationalarchives.csv.validator.api.java```のパッケージの方で
コンパイルが通りました。

```java:本家のコードを少し修正
		List<Substitution> pathSubstitutions = new ArrayList<>();

		// CSV検証の実行
		List<FailMessage> messages = CsvValidator.validate("invalid.csv",// CSVファイル
				"example.csvs", // CSVスキーマファイル
				false, // 最初にエラーが発生したら処理を中断するか
				pathSubstitutions, // ファイルパスの格納用
				true // ファイルパスの厳格なチェック
				);

		if (messages.isEmpty()) {
			System.out.println("Completed validation OK");
			return;
		}
		
		for (FailMessage message : messages) {
			if (message instanceof WarningMessage) {
				System.out.println("[WARN] " + message.getMessage());
			} else {
				System.out.println("[ERROR] " + message.getMessage());
			}
		}
```

## 実行結果
想定通り、3項目が定義違反で怒られています。

 * 1行目のageの値が0~120の間にない
 * 3行目のカラム数が5個ある
 * 3行目のgenderがm,f,t,nの値ではない

まだまだ改善の余地はありそうですが、内容としては十分ではないでしょうか。

```console:実行結果
[ERROR] range(0,120) fails for line: 1, column: age, value: "4 years"
[ERROR] Expected @totalColumns of 3 and found 5 on line 3
[ERROR] is("m") or is("f") or is("t") or is("n") fails for line: 3, column: gender, value: "malename"
```


# おしまい

* CSV Schema、もっと流行ると良いですね！
* CSVもっと使いましょう！XMLでのデータ交換は滅びろ。