ホーム 記事一覧 ブック DH週間トピックス 検索 このサイトについて
English
Dydra JSON-LDシリアライゼーションの挙動と回避策

Dydra JSON-LDシリアライゼーションの挙動と回避策

概要 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 ライブラリが必要です: ...

IIIF認証API 2.0の動作確認

IIIF認証API 2.0の動作確認

概要 以下のIIIF認証API 2.0の動作確認を行う機会がありましたので、備忘録です。 https://iiif.io/api/auth/2.0/ 以下のようなデモサイトを作成しました。 https://iiif-auth-nextjs.vercel.app/ja リポジトリは以下です。 https://github.com/nakamura196/iiif-auth-nextjs 以下、AIによる説明です。なお、Miradorではうまく動作させることができなかったため、今後の課題です。 概要 本記事では、IIIF Authentication API 2.0 の認証フローを、実際のHTTPリクエスト/レスポンスのレベルで詳細に解説します。各ステップでどのようなリクエストが送信され、どのようなレスポンスが返されるのかを追跡していきます。 アーキテクチャ概要 ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Client │────▶│ IIIF Server │────▶│Auth Service │ │ (Browser) │◀────│ │◀────│ │ └─────────────┘ └─────────────┘ └─────────────┘ 認証フローの詳細 Step 1: 初回の画像情報リクエスト(未認証) リクエスト: GET /api/iiif/image/sample/info.json HTTP/1.1 Host: localhost:3001 Accept: application/json 処理フロー(サーバー側): // app/api/iiif/image/[id]/info.json/route.ts export async function GET(request: NextRequest) { // 1. Authorizationヘッダーを確認 const authHeader = request.headers.get('authorization'); let token = authHeader?.replace('Bearer ', ''); // 2. クエリパラメータもチェック(フォールバック) if (!token) { token = request.nextUrl.searchParams.get('token') || ''; } // 3. トークンの検証 const isValid = token ? await verifyToken(token) : null; // 4. 未認証の場合は401を返す if (!isValid) { return NextResponse.json({ error: 'Authentication required', service: [{ "@context": "http://iiif.io/api/auth/2/context.json", "id": `${request.nextUrl.origin}/api/iiif/probe`, "type": "AuthProbeService2" }] }, { status: 401 }); } } レスポンス: ...

DrupalのJSON:APIでcreatedやchangedに対するフィルタを適用する

DrupalのJSON:APIでcreatedやchangedに対するフィルタを適用する

概要 DrupalのJSON:APIでcreatedやchangedに対するフィルタを適用する方法の備忘録です。 背景 以下を参考にしました。 https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/filtering 例えば、6/2以降に更新されたものだけをフィルタリングしようとした際、以下のクエリでは適切に動作しませんでした。 ?filter[a-label][condition][path]=changed&filter[a-label][condition][operator]=%3E%3D&filter[a-label][condition][value]=2025-06-02 正しい方法 以下の記事が参考になりました。 https://www.reddit.com/r/drupal/comments/1bdvu61/json_api_drupal_filter_on_date/ Note that timestamp fields (like created or changed) currently must use a timestamp for filtering: タイムスタンプフィールド(createdやchangedなど)は現在、フィルタリングにタイムスタンプを使用する必要があります。 例えば、2025/6/2のタイムスタンプ1748790000を用いて、以下のようなクエリを使用することで、正しくフィルタリングできました。 ?filter[a-label][condition][path]=changed&filter[a-label][condition][operator]=%3E%3D&filter[a-label][condition][value]=1748790000 まとめ DrupalのJSON:APIで、createdやchangedに対するフィルタを適用する際にお役に立てば幸いです。

DrupalでJSON形式のFieldを扱うための「JSON Field」モジュールを使用する

DrupalでJSON形式のFieldを扱うための「JSON Field」モジュールを使用する

概要 DrupalでJSON形式のFieldを扱うための「JSON Field」モジュールを使用する機会がありましたので、備忘録です。 https://www.drupal.org/project/json_field 結果、以下のようにエディタと共にJSONを扱えるようになりました。 インストール 以下により、ダウンロードします。 composer require 'drupal/json_field:^1.4' drush en json_field さらに、以下により、ウィジェットも有効します。 drush en json_field_widget GUIから有効にする場合には、以下の2つを有効にします。 設定 コンテンツタイプのフィールドの管理において、JSONフィールドを追加します。 そして、「フォームの表示管理」において、ウィジェットを選択します。 結果、コンテンツの編集画面で、以下のようなフォームが表示されます。 まとめ DrupalでJSONを管理するにあたり、参考になりましたら幸いです。

DTS (Distributed Text Services)のビューア開発

DTS (Distributed Text Services)のビューア開発

概要 DTS (Distributed Text Services)のビューアを開発したので、備忘録です。 以下のURLからお試しいただけます。 https://dts-viewer.vercel.app/ja/ 背景 DTS (Distributed Text Services)の公式ページは以下です。 https://distributed-text-services.github.io/specifications/ 以下の記事でも取り上げました。 今回、このDTS仕様に一部準拠したビューアを開発しました。 使い方 以下がトップページです。フォームにDTSのURLを入力します。ページ下部で例を提供します。技術的には、Entry pointを使用しています。 コレクションの一覧ページです。Collection Endpointを使用しています。 以下のAPIを例としています。 リンクをたどると、以下のようなリソースの一覧ページに遷移します。 ダウンロードボタンを押すと、TEI/XMLが表示されます。Document Endpointを使用しています。 ナビゲーションボタンを押すと、アクセス可能な部分テキストの一覧が表示されます。Navigation Endpointを使用していますが、現時点で複数階層には非対応です。 リンクをクリックすると、以下のような部分テキストをダウンロードすることができます。 工夫点 公式ページに以下のように記載されています。 The DTS Specification is currently in a public comment period following the 1-alpha release (機械翻訳)DTS仕様は、1-alphaリリースの後、現在パブリックコメント期間中です。 このような背景のため、既存のDTSの記述方法にばらつきがありました。そこで内部でできるだけDTS API (1.0 Draft)に変換し、その結果を可視化するようにしています。 DTS仕様が成熟するにつれ、このような問題は解決されるかと思います。 まとめ DTS仕様は以下のように説明されています。 The Distributed Text Services (DTS) Specification defines an API for working with collections of text as machine-actionable data. ...

The New York Public LibraryのAPIを使ってみる

The New York Public LibraryのAPIを使ってみる

概要 The New York Public Libraryでは、Digital Collections APIを提供しています。 http://api.repo.nypl.org/ 本記事では、このAPIの使い方の一例について説明します。 サインアップ まず以下のリンクをクリックして、サインアップを行います。 以下のようなフォームが表示されますので、必要な情報を入力します。 入力後、 Welcome to NYPL API という件名のメールが届きます。このメールの中に、 Authentication Token が記載されています。 メタデータの抽出 The New York Public Library Digital Collections APIではさまざまなendpointが提供されています。今回は以下のendpointを利用して、各アイテムのメタデータを抽出してみます。 http://api.repo.nypl.org/api/v1/items/item_details/[:id] 具体的には、以下のアイテムを例としてます。 https://digitalcollections.nypl.org/items/510d47e1-d3b0-a3d9-e040-e00a18064a99 そして、以下に示したメタデータの抽出を試みます。 以下のGoogle Colabにメタデータの抽出サンプルプログラムを作成しました。参考になりましたら幸いです。 https://colab.research.google.com/drive/1sO9plTqraPwdBF61sArlD6k6pZRpfJL8?usp=sharing 上記プログラムを実行すると、例えば以下のようなメタデータが得られます。 { "title": "Genji monogatari: Sakaki no maki|The Tale of Genji", "collection": " > Genji monogatari : Sakaki no maki", "dateIssued": "1650 (start)|1700 (end)", "place": "Kyoto", "identifier": "Hades Collection Guide ID (legacy): 443|Hades struc ID (legacy): 747877|Universal Unique Identifier (UUID): ce4bcd90-c60d-012f-9d19-58d385a7bc34" } まとめ APIを利用したデータ活用の参考になりましたら幸いです。 ...