JSON操作

JSONファイル/JSONデータの読み書き等操作を扱います。 ここでは、標準で備えているstd.jsonモジュールと、その中のJSONValueについて説明します。 このモジュールは、あくまでもJSONが最低限取り扱える程度の機能があって、速度や利便性は二の次です。 ほかにもサードパーティのライブラリとして、asdfを代表として、より高度な取り扱いができるライブラリがあります。
Examples:

文字列⇔JSON

import std.json;
// 文字列からJSONValueを作成します。
auto jv = JSONValue("文字列");

// JSONValueからJSONの文字列表現を取得したい場合は
// `.toString()`メソッドを使用します。
assert(jv.toString() == `"文字列"`);
// JSONValueが文字列であることを確認するには、`.type`プロパティを使用して
// 以下のように`JSONType.string`と比較します。
assert(jv.type == JSONType.string);
// JSONValueから文字列を取り出す場合は、`.str`プロパティを使用して
// 以下のようにします。
assert(jv.str == "文字列");
Examples:

数値⇔JSON

import std.json;
import std.exception: assertThrown;
import std.math: isClose;

// 数値(符号あり・符号なし)からJSONValueを作成します。
int  x = -128;
uint y = 0xff;
auto jvx = JSONValue(x);
auto jvy = JSONValue(y);

// 浮動小数点数からJSONValueを作成します。JSONにおけるnumberはdouble相当です。
double z = 1.25;
auto jvz = JSONValue(z);

// JSONValueからJSONの文字列表現を取得したい場合は
// `.toString()`メソッドを使用します。
assert(jvx.toString() == `-128`);
assert(jvy.toString() == `255`);
assert(jvz.toString() == `1.25`);

// JSONValueが数値であることを確認するには、`.type`プロパティを使用して、
// 以下のように`JSONType.integer`や`JSONType.uinteger`、あるいは`JSONType.float_`と比較します。
assert(jvx.type == JSONType.integer);
assert(jvy.type == JSONType.uinteger);
assert(jvz.type == JSONType.float_);

// JSONValueから数値を取り出す場合は、以下のように
// `.integer`/`.uinteger`/`.floating`プロパティを使用します。
assert(jvx.integer == -128);
assert(jvy.uinteger == 255);
assert(isClose(jvz.floating, 1.25)); // 浮動小数点数はisCloseで比較します

// ちなみに、型を符号ありとなしで間違ってしまうと例外が投げられます
assertThrown(jvx.uinteger == cast(uint)-128);

// 符号ありのつもりなんだけど(十分格納可能な範囲の)符号なしも
// 受け付けたい場合は以下のようにします (とても面倒)
long w = jvy.type == JSONType.integer  ? jvy.integer
       : jvy.type == JSONType.uinteger ? jvy.uinteger : 0;
Examples:

真偽値・null⇔JSON

真偽値(true / false)とnullの状態は、上記文字列や数値とは異なり、 それぞれ個別にJSONTypeが存在します。
import std.json;
import std.exception: assertThrown;

// JSONValueを作る
auto jvf = JSONValue(false);
auto jvt = JSONValue(true);
auto jvn = JSONValue(null);

// JSONValueからJSONの文字列表現を取得したい場合は
// `.toString()`メソッドを使用します。
assert(jvf.toString() == `false`);
assert(jvt.toString() == `true`);
assert(jvn.toString() == `null`);

// 真偽は`.type`で調べます
assert(jvf.type == JSONType.false_);
assert(jvt.type == JSONType.true_);
// さすがに↑だけではつらい(というか不格好な)ので、
// ↓でOKなようにプロパティがあります
assert(!jvf.boolean);
assert( jvt.boolean);

// nullかどうかも`.type`で調べます
assert(jvn.type == JSONType.null_);
// is nullでは調べられません(コンパイルエラーが出ます)
assert(!__traits(compiles, {
    assert(jvn is null);
}));
Examples:

配列⇔JSON

import std.json;
import std.exception: assertThrown;
import std.string: chompPrefix, outdent;

// JSONValueを作る
auto jv1 = JSONValue([1,2,3,4,5]);
// 値にはJSONValueを使うことで、数値と文字列などを混成できます。
auto jv2 = JSONValue([JSONValue(1), JSONValue("弐")]);

// JSONの文字列表現は、`.toString()`メソッドで得られます。
assert(jv1.toString() == `[1,2,3,4,5]`);
assert(jv2.toString() == `[1,"弐"]`);

// 配列ともなると文字列表現は整形したくなってきます。
// `toPrettyString()`メソッドで整形された文字列が得られます。
assert(jv2.toPrettyString() ==`
[
    1,
    "弐"
]`.chompPrefix("\n").outdent());

// 連想配列かどうかは、`.type`と`JSONType.array`を比較します
assert(jv1.type == JSONType.array);
assert(jv2.type == JSONType.array);

// 値へのアクセスは`[]`演算子オーバーロードを使うか、
// `.array`プロパティを使います
// ただし、`.array`プロパティはなぜか`@system`らしいです
() @trusted {
    assert(jv1.array[0].type == JSONType.integer);
    assert(jv1.array[0].integer == 1);
} ();
assert(jv2[1].type == JSONType.string);
assert(jv2[1].str == "弐");
Examples:

連想配列⇔JSON

import std.json;
import std.exception: assertThrown;
import std.string: chompPrefix, outdent;

// JSONValueを作る
auto jv1 = JSONValue([
    "いち": 1, "に": 2]);
// 文字列以外はキーにできません。
assert(!__traits(compiles, {
    auto jv2 = JSONValue([
        1: "いち", 2: "に"]);
}));
assert(!__traits(compiles, {
    auto jv2 = JSONValue([
        true: "真", false: "偽"]);
}));
// 値にはJSONValueを使うことで、数値と文字列などを混成できます。
auto jv2 = JSONValue([
    "いち": JSONValue(1), "に": JSONValue("弐")]);
// キーは無理です
assert(!__traits(compiles, {
    auto jv3 = JSONValue([
        JSONValue(true): JSONValue("真"),
        JSONValue(false): JSONValue("偽")]);
}));


// JSONの文字列表現は、`.toString()`メソッドで得られます。
// ただし、キーの並び順は不定(unordered)です。
auto str = jv1.toString();
assert(str == `{"いち":1,"に":2}` || str == `{"に":2,"いち":1}`);

// 連想配列ともなると文字列表現は整形したくなってきます。
// `toPrettyString()`メソッドで整形された文字列が得られます。
assert(JSONValue(["壱": 1]).toPrettyString() ==`
{
    "壱": 1
}`.chompPrefix("\n").outdent());

// 連想配列かどうかは、`.type`と`JSONType.object`を比較します
assert(jv1.type == JSONType.object);
assert(jv2.type == JSONType.object);

// 値へのアクセスは`[]`演算子オーバーロードを使うか、
// `.object`プロパティを使います
// ただし、`.object`プロパティはなぜか`@system`らしいです
() @trusted {
    assert(jv1.object["いち"].type == JSONType.integer);
    assert(jv1.object["いち"].integer == 1);
} ();
assert(jv2["に"].type == JSONType.string);
assert(jv2["に"].str == "弐");
Examples:

JSONファイルの書き込み・読み込み

import std.json;
import std.file;
enum jsonTestFile = "json_example_filerw_test.json";

// あとしまつ
scope (exit)
{
    if (jsonTestFile.exists)
        remove(jsonTestFile);
}

// JSONValueを作る
auto jv1 = JSONValue([
    "aaa": JSONValue([1UL,2UL,3UL]),
    "bbb": JSONValue([
        "bbb-1": 1,
        "bbb-2": 2,
    ]),
    "ccc": JSONValue(null)
]);

// JSONValueをファイルに保存します。
// 単純に`toString()`メソッドや`toPrettyString()`メソッドで
// JSONの文字列表現に直した後`std.file`などのファイル書き込みを行います。
std.file.write(jsonTestFile, jv1.toPrettyString());

// JSONファイルを読み込んで、JSONValueを得ます
// というか、ファイルから読み込んだJSONの文字列表現を、`parseJSON()`関数
// でJSONValueに変換します。
// curlなどを使って取得したHTTPレスポンスを解析するときも同様に`parseJSON()`
// 関数でJSONValueに変換できます。
auto jv2 = std.file.readText(jsonTestFile).parseJSON();

// ちゃんと読み込まれています
assert(jv1["aaa"][0] == jv2["aaa"][0]);
// ただし、注意点。
// 一度保存された整数は、符号なし→符号ありになることがあります
assert(jv1["aaa"][0].type != jv2["aaa"][0].type);
assert(jv1["aaa"][0].type == JSONType.uinteger);
assert(jv2["aaa"][0].type == JSONType.integer);