Progress Log: JSON形式についての調査と整理¶
Task Description¶
JSON (JavaScript Object Notation) 形式について、以下の観点から調査・整理をしました。
- JSONについての基本情報
- JSON形式のサンプル
- JSON形式の利用が適している分野
- JSON形式の派生形式
- JSON形式を扱うライブラリ(C/C++とPython)
Outcome¶
JSON形式に関する包括的なまとめを作成しました。
JSONについて¶
JSON (JavaScript Object Notation) は、テキストベースの軽量なデータ交換形式です。
主な特徴:
- シンプルで読みやすい: テキストベースで人間が読みやすい構文
- 言語中立: JavaScriptの構文に基づくが、すべてのプログラミング言語で解析可能
- 軽量: XMLと比較してファイルサイズが小さい
- 構造化: オブジェクト(連想配列)と配列をサポート
- 標準化: RFC7158で標準化されている
基本データ型:
- オブジェクト:
{ "key": value, ... }- キー値ペアの集合 - 配列:
[ value, value, ... ]- 値の順序付きリスト - 文字列:
"text"- ダブルクォートで囲まれた文字列 - 数値:
123,45.67,1.23e-4- 整数と小数、科学記法 - 真偽値:
true,false- 論理値 - null:
null- 空の値
JSON形式のサンプル¶
シンプルなオブジェクト:
{
"name": "OSECHI",
"type": "cosmic-ray-detector",
"version": "1.6.1",
"enabled": true
}
ネストされたオブジェクト:
{
"device": {
"name": "OSECHI",
"board": "ESP32-WROOM-32E",
"specs": {
"cpu_cores": 2,
"clock_mhz": 240,
"ram_kb": 320,
"flash_mb": 4
}
}
}
配列を含む例:
{
"detections": [
{
"timestamp_us": 1234567890,
"signal1": 95,
"signal2": 87,
"signal3": 92,
"temperature": 25.35,
"pressure": 1013.25,
"humidity": 45.67
},
{
"timestamp_us": 1234568901,
"signal1": 100,
"signal2": 78,
"signal3": 85,
"temperature": 25.40,
"pressure": 1013.28,
"humidity": 45.72
}
]
}
センサー構成データ:
{
"configuration": {
"detector": {
"poll_count": 100,
"channels": [
{"id": 1, "pin": 12, "threshold": 50},
{"id": 2, "pin": 19, "threshold": 50},
{"id": 3, "pin": 27, "threshold": 50}
]
},
"sensors": {
"bme280": {"address": "0x76", "enabled": true},
"adc": {"pin": 32, "resolution": 12}
}
}
}
JSON形式の利用が適している分野¶
- API通信: REST APIのリクエスト/レスポンス形式として最も広く使用
- 設定ファイル: アプリケーション設定の保存(例:
package.json,tsconfig.json) - ロギング: 構造化ログの標準フォーマット
- データストレージ: NoSQLデータベース(MongoDB, Firebaseなど)
- IoT/組込み: センサーデータの送受信フォーマット
- Webデータ交換: ブラウザとサーバー間のデータ交換
- メタデータ: プロジェクトメタデータ、パッケージ情報の記述
- キャッシュ: メモリキャッシュやキャッシュファイルの格納
JSON形式の派生¶
JSONL (JSON Lines)
- 1行に1つのJSON オブジェクトを記述
- ストリーミング処理やログファイルに適している
- 例:
{"id": 1, "name": "Event1", "value": 95}
{"id": 2, "name": "Event2", "value": 87}
NDJSON (Newline Delimited JSON)
- JSONLと同じ仕様(別名称)
- データストリーム処理に最適
JSON5
- JSONの拡張仕様
- コメント、末尾のカンマ、シングルクォート対応
- 開発用設定ファイルで使用
JSONP (JSON with Padding)
- クロスオリジンでのデータ取得(古い方式、現在はCORSが主流)
- コールバック関数でJSONを囲む
- 例:
callback({"key": "value"})
Canonical JSON
- JSON要素の順序を固定化したバージョン
- 暗号署名や検証が必要な場合に使用
JSON形式を扱うライブラリ¶
Python¶
標準ライブラリ:
json- Python標準モジュール
import json
# JSON文字列をPythonオブジェクトに変換
data = json.loads('{"name": "OSECHI", "version": "1.6.1"}')
# PythonオブジェクトをJSON文字列に変換
json_str = json.dumps(data, indent=2)
# ファイルから読み込み
with open('config.json', 'r') as f:
config = json.load(f)
# ファイルに書き込み
with open('output.json', 'w') as f:
json.dump(data, f, indent=2)
高機能ライブラリ:
orjson- 高速なJSON処理(C実装)
import orjson
data = orjson.loads(json_bytes)
json_bytes = orjson.dumps(data)
ujson- マイクロPythonのJSON実装-
マイクロコントローラーでのJSON処理に最適
-
pydantic- JSON スキーマ検証
from pydantic import BaseModel
class Detection(BaseModel):
signal1: int
signal2: int
temperature: float
C/C++¶
軽量ライブラリ:
cJSON- シンプルで軽量(C実装)
#include "cjson/cJSON.h"
cJSON *root = cJSON_Parse(json_string);
cJSON *item = cJSON_GetObjectItem(root, "name");
cJSON_Delete(root);
jansson- C言語用の高品質JSONライブラリ
#include <jansson.h>
json_t *root = json_loads(json_string, 0, &error);
json_object_set_new(root, "key", json_string("value"));
高機能ライブラリ:
RapidJSON- 高速なC++JSONライブラリ
#include "rapidjson/document.h"
rapidjson::Document doc;
doc.Parse(json_string);
const char* name = doc["name"].GetString();
nlohmann/json- モダンC++ JSON ライブラリ
#include <nlohmann/json.hpp>
using json = nlohmann::json;
json data = json::parse(json_string);
std::cout << data["name"] << std::endl;
Boost.JSON- Boost C++ライブラリのJSON実装- 高度な機能と最適化
組込み/マイコン向け:
ArduinoJson- Arduino/ESP32向けの軽量JSONライブラリ
#include <ArduinoJson.h>
StaticJsonDocument<256> doc;
deserializeJson(doc, json_string);
int value = doc["value"];
doc["name"] = "OSECHI";
String output;
serializeJson(doc, output);
JSONL形式をKurikintonsで使う場合¶
JSONL(JSON Lines)形式は、Kurikintonsの検出データロギングにとくに適しています。
利点:
- ストリーミング処理に最適: 各検出イベントごとに1行のJSONオブジェクトを記録
- ESP32がリアルタイムでファイルに追記可能
- 完全なファイルパースが不要(パーシャルデータに強い)
-
メモリ使用量が最小限(1行ずつ処理可能)
-
検出イベント履歴に最適: 時系列データの記録として自然
- 新しい検出データが行追加されていく形式
-
古いデータと新しいデータの混在が容易
-
データ解析が簡単: Python での処理が直感的
for line in file: data = json.loads(line)で読める-
grep/awk などのテキストツールと相性良好
-
リアルタイム監視に対応: ログファイル監視ツールと連携可能
tail -fでリアルタイム追従可能- ストリーミング解析に向いている
Kurikintons での想定用途:
ファイル構成例(detection_log.jsonl):
{"timestamp_us": 1000000, "signal1": 95, "signal2": 87, "signal3": 92, "adc": 2048, "temp": 25.35, "pressure": 1013.25, "humidity": 45.67}
{"timestamp_us": 2500000, "signal1": 100, "signal2": 78, "signal3": 85, "adc": 1950, "temp": 25.40, "pressure": 1013.28, "humidity": 45.72}
{"timestamp_us": 4100000, "signal1": 88, "signal2": 92, "signal3": 79, "adc": 2100, "temp": 25.38, "pressure": 1013.26, "humidity": 45.70}
ESP32 側の実装想定:
// ArduinoJson を使用して JSONL 形式で記録
#include <ArduinoJson.h>
void log_detection_to_jsonl(const cosmic_detection_t& detection) {
StaticJsonDocument<256> doc;
doc["timestamp_us"] = micros();
doc["signal1"] = detection.signal1;
doc["signal2"] = detection.signal2;
doc["signal3"] = detection.signal3;
doc["adc"] = adc_value;
doc["temp"] = temperature;
doc["pressure"] = pressure;
doc["humidity"] = humidity;
// シリアル出力または SDカード に追記
serializeJson(doc, Serial);
Serial.println(); // 改行で1行を終了
}
Python 側での解析例:
import json
from datetime import datetime
def analyze_detection_log(filename):
"""JSONL ログファイルを解析"""
detections = []
with open(filename, 'r') as f:
for line in f:
if line.strip():
data = json.loads(line)
detections.append(data)
# 検出数
print(f"Total detections: {len(detections)}")
# 平均信号値
avg_signal1 = sum(d['signal1'] for d in detections) / len(detections)
print(f"Average signal1: {avg_signal1:.2f}")
# イベント間隔分析
for i in range(1, len(detections)):
interval_us = detections[i]['timestamp_us'] - detections[i-1]['timestamp_us']
interval_ms = interval_us / 1000
print(f"Event {i}: {interval_ms:.2f} ms interval")
return detections
# リアルタイムログ監視
def monitor_log_file(filename):
"""新しい検出をリアルタイム監視"""
import time
last_pos = 0
while True:
with open(filename, 'r') as f:
f.seek(last_pos)
for line in f:
if line.strip():
data = json.loads(line)
print(f"Detected: {data['signal1']}, {data['signal2']}, {data['signal3']}")
last_pos = f.tell()
time.sleep(1)
利点の総括:
- ストリーミング: JSONL形式は追記が簡単、通常のJSON配列は再パースが必要
- メモリ効率: JSONL形式は1行ずつ処理、通常のJSON配列は全体をパース
- ファイルサイズ: ほぼ同等
- テキストツール連携: JSONL形式はgrep/awk可能、通常のJSON配列は複雑
- 人間可読性: JSONL形式は行ごとに読める、通常のJSON配列は構造が見やすい
データ量と送信時間の比較¶
現在のフォーマット(スペース区切り):
signal1 signal2 signal3 adcValue temperature pressure humidity [uptime_ms] [duration_us]
100 85 92 2048 25.35 1013.25 45.67 123456 1234567890
スペース区切り形式(現在)¶
サンプルデータ:
100 85 92 2048 25.35 1013.25 45.67 123456 1234567890
データ量計算:
- 数値部:
100 85 92 2048 25.35 1013.25 45.67 123456 1234567890 - スペース: 8個
- 改行: 1文字(\n)
- 合計: 59バイト
JSONL形式¶
サンプルデータ:
{"signal1":100,"signal2":85,"signal3":92,"adc":2048,"temp":25.35,"pressure":1013.25,"humidity":45.67,"uptime_ms":123456,"duration_us":1234567890}
データ量計算:
- キー:
signal1,signal2,signal3,adc,temp,pressure,humidity,uptime_ms,duration_us - 値: 数値部
- JSON構造:
{,},:,,など - 改行: 1文字(\n)
- 合計: 151バイト
形式別比較¶
1レコード当たりのデータ量:
- スペース区切り形式: 59バイト
- JSONL形式: 151バイト
- オーバーヘッド(スペース区切り): 8バイト(スペース+改行)
- オーバーヘッド(JSONL形式): 92バイト(JSON構造+キー)
- オーバーヘッド率(スペース区切り): 13.6%
- オーバーヘッド率(JSONL形式): 61.0%
大規模データでの比較(検出頻度1Hz = 1秒に1回):
- 1時間: スペース区切り59KB、JSONL形式151KB
- 1日: スペース区切り1.4MB、JSONL形式3.6MB
- 1ヶ月: スペース区切り42MB、JSONL形式108MB
送信時間の計算¶
ボーレート: 115200 bps(9600バイト/秒)
単一レコード送信時間:
- スペース区切り形式(59バイト): 6.1ms
- JSONL形式(151バイト): 15.7ms
- 時間差: +9.6ms(16.3%増加)
実運用での送信スループット:
- 低頻度検出の場合(検出頻度: 1回/分):
- スペース区切り形式: 約118バイト/分
- JSONL形式: 約302バイト/分
-
帯域幅への影響: ほぼ無視できる(115.2kbpsに対して数百バイト/分)
-
高頻度検出の場合(検出頻度: 1回/秒):
- スペース区切り形式: 約3.5KB/分
- JSONL形式: 約9.1KB/分
- 帯域幅への影響: 依然として軽微(115.2kbpsに対して9KB/分)
結論¶
- データ量増加: 約2.6倍増加するが、ボーレート115200では実運用への影響はほぼ無視できる
- 利点: ストリーミング処理、解析の容易さがデメリット(データ量増)を大きく上回る
- 推奨: JSONLへの移行は十分に実行可能で、設計上のメリットが大きい
Learnings¶
- JSONは通信フォーマットとして最適: テキストベース、軽量、言語中立で多くのシステムに対応
- 派生形式が用途に応じて存在: JSONLはログストリーミング、JSON5は開発用設定に適している
- C/C++では複数の選択肢がある: 軽量(cJSON)から高機能(RapidJSON, nlohmann/json)まで用途に応じて選択可能
- ESP32/Arduino向けにはArduinoJsonが標準: マイコンのメモリ制約に最適化されている
- Pythonはjsonモジュールで十分: 標準ライブラリで大抵のケースに対応可能
- Kurikintons には JSONL 形式が最適: ストリーミング検出データの記録・解析に理想的
Next Steps¶
- KurikintonsでのJSONL実装検討:
- ArduinoJsonを使用したJSONL出力機能の設計
- マイコン側でのシリアル出力をJSONL形式に統一
-
SDカードへのJSONLファイル保存機能(オプション)
-
Python解析スクリプト開発:
- JSONLログファイルの自動解析ツール
- リアルタイムログ監視スクリプト
-
グラフ描画による検出パターン分析
-
テスト/ベンチマーク:
- JSONL形式でのシリアル出力パフォーマンステスト
- 大規模検出ログの解析性能確認
- メモリ使用量の実測定