概要
Dydraは優れたクラウドベースのRDFトリプルストアですが、JSON-LDシリアライゼーションにおいて、一部のケースで期待と異なる出力が得られることがあります。このブログでは、その挙動と、我々が実装した回避策について解説します。
確認された挙動
期待される出力
JSON-LD仕様では、URI参照は以下のようにオブジェクト形式で出力されることが一般的です:
{
"@id": "https://example.com/item/1",
"@type": ["prov:Entity"],
"prov:wasAttributedTo": {
"@id": "https://sepolia.etherscan.io/address/0x1234..."
},
"prov:wasGeneratedBy": {
"@id": "https://sepolia.etherscan.io/tx/0xabcd..."
}
}
Dydraで確認された出力
DydraのJSON-LDエンドポイントでは、一部のURI参照が単なる文字列として出力されるケースが確認されました:
{
"@id": "https://example.com/item/1",
"@type": ["prov:Entity"],
"prov:wasAttributedTo": "https://sepolia.etherscan.io/address/0x1234...",
"prov:wasGeneratedBy": "https://sepolia.etherscan.io/tx/0xabcd..."
}
注意 : この挙動は全てのプロパティで発生するわけではなく、@contextの定義やプロパティの種類によって異なる場合があります。
挙動の違いによる影響
| 形式 | JSON-LDパーサーの解釈 |
|---|---|
{ "@id": "..." } | URI参照(他ノードへのリンク) |
"..." | リテラル文字列 |
この違いにより、以下の影響が生じる可能性があります:
- グラフ構造のトラバーサルに影響
- 一部のSPARQLクエリ結果に影響
- JSON-LDフレーミング処理に影響
型付きリテラルについて
同様に、xsd:dateTime などの型付きリテラルでも型情報が省略されるケースがあります。
期待される出力 :
{
"prov:startedAtTime": {
"@value": "2025-01-15T10:30:00Z",
"@type": "xsd:dateTime"
}
}
確認された出力 :
{
"prov:startedAtTime": "2025-01-15T10:30:00Z"
}
回避策
アプローチ:TTL形式で取得してJSON-LDを構築
DydraはTurtle (TTL) 形式では正確にシリアライズするため、以下の戦略を採用しました:
[クライアント]
│
│ Accept: text/turtle
v
[Dydra SPARQL Endpoint]
│
│ TTL形式で返却
v
[n3パーサー]
│
│ Quadsに変換
v
[JSON-LD構築ロジック]
│
│ 正しいJSON-LD
v
[アプリケーション]
実装
import { Parser } from "n3";
/**
* TTLをパースしてJSON-LDに変換
* DydraのJSON-LDシリアライゼーションの挙動を回避
*/
function turtleToJsonLd(turtle: string): RDFGraph {
const parser = new Parser();
const quads = parser.parse(turtle);
// Subject別にトリプルをグループ化
const subjects = new Map<string, Map<string, unknown[]>>();
for (const quad of quads) {
const subjectId = quad.subject.value;
if (!subjects.has(subjectId)) {
subjects.set(subjectId, new Map());
}
const predicates = subjects.get(subjectId)!;
const predicateId = quad.predicate.value;
if (!predicates.has(predicateId)) {
predicates.set(predicateId, []);
}
// オブジェクトの値を型情報付きで構築
let objectValue: unknown;
if (quad.object.termType === "NamedNode") {
// URI参照: { "@id": "..." } ← ここがポイント
objectValue = { "@id": quad.object.value };
} else if (quad.object.termType === "Literal") {
const literal = quad.object;
if (literal.language) {
// 言語タグ付きリテラル
objectValue = { "@value": literal.value, "@language": literal.language };
} else if (literal.datatype &&
literal.datatype.value !== "http://www.w3.org/2001/XMLSchema#string") {
// 型付きリテラル(xsd:string以外)
objectValue = { "@value": literal.value, "@type": literal.datatype.value };
} else {
// プレーンリテラル
objectValue = literal.value;
}
} else if (quad.object.termType === "BlankNode") {
objectValue = { "@id": `_:${quad.object.value}` };
} else {
objectValue = quad.object.value;
}
predicates.get(predicateId)!.push(objectValue);
}
// JSON-LD @graphを構築
const graph: Array<Record<string, unknown>> = [];
for (const [subjectId, predicates] of subjects) {
const node: Record<string, unknown> = { "@id": subjectId };
for (const [predicateId, objects] of predicates) {
if (predicateId === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") {
// @typeは特別扱い
node["@type"] = objects.map((o) => {
if (typeof o === "object" && o !== null && "@id" in o) {
return (o as { "@id": string })["@id"];
}
return o;
});
} else {
// 単一値の場合は配列から取り出す
node[predicateId] = objects.length === 1 ? objects[0] : objects;
}
}
graph.push(node);
}
return {
"@context": JSONLD_CONTEXT,
"@graph": graph,
};
}
使用例
// TTL形式で取得して変換
const response = await fetch(`${DYDRA_ENDPOINT}/sparql`, {
method: "POST",
headers: {
"Accept": "text/turtle", // TTLで取得
},
body: query,
});
const turtle = await response.text();
const jsonld = turtleToJsonLd(turtle); // JSON-LDに変換
依存ライブラリ
この回避策には n3 ライブラリが必要です:
npm install n3
npm install --save-dev @types/n3
まとめ
| 項目 | 内容 |
|---|---|
| 確認された挙動 | 一部のURI参照が { "@id": "..." } ではなく文字列になる |
| 発生条件 | 全てのケースではなく、コンテキストやプロパティにより異なる |
| 回避策 | TTL形式で取得し、n3でパースしてJSON-LD構築 |
| 追加依存 | n3 |
Dydraを使用する際、JSON-LDの出力形式が期待と異なる場合は、TTL形式で取得してクライアント側で変換する方法が有効です。
参考リンク
- Dydra - クラウドRDFストア
- JSON-LD 1.1 - W3C仕様
- N3.js - JavaScript RDFパーサー
- RDF 1.1 Turtle - Turtle構文仕様
Last updated: 2025-12-29