はじめに

本章では、校異源氏物語テキストDBで公開しているTEI/XMLファイルに対するDTS(Distributed Text Services)APIの設計と実装について解説します。DTSは、構造化されたテキストコレクションへの統一的なアクセス手段を提供するAPI仕様であり、TEI/XMLで符号化されたテキストの配信に特に適しています。

DTS(Distributed Text Services)とは

DTSは、デジタル・ヒューマニティーズの分野で策定されたテキスト配信のためのAPI仕様です。古典テキストの配信規格として広く用いられてきたCTS(Canonical Text Services)を発展させたもので、以下の3つのエンドポイントから構成されます。

3つのエンドポイント

  1. Collection エンドポイント (/collection): テキストのコレクション情報を提供します。階層構造を持つコレクションの一覧やメタデータを返却します。
  2. Navigation エンドポイント (/navigation): テキストの内部構造(巻、章、段落など)の情報を提供します。テキストのどの部分をどのように参照できるかを示します。
  3. Document エンドポイント (/document): テキスト本体を取得するためのエンドポイントです。特定の範囲を指定してTEI/XMLのテキストデータを返却します。

CTSとの関係

DTSは、CTSの概念を継承しつつ、JSON-LDやLinked Dataの仕組みを取り入れた現代的なAPI仕様です。CTSがXML-RPCベースのプロトコルだったのに対し、DTSはRESTful APIとしてWebの標準的なアーキテクチャに沿った設計になっています。CTSで使われていたURNベースのテキスト参照体系もDTSでサポートされています。

校異源氏物語テキストDBの構造

校異源氏物語テキストDBは、源氏物語の校異(テキストの異同)をTEI/XMLで符号化したデータベースです。

TEI/XMLファイルの構造

校異源氏物語のTEI/XMLファイルは、TEI Critical Apparatusの仕様に基づいてマークアップされています。

<TEI xmlns="http://www.tei-c.org/ns/1.0">
  <teiHeader>
    <fileDesc>
      <titleStmt>
        <title>校異源氏物語 桐壺</title>
      </titleStmt>
    </fileDesc>
  </teiHeader>
  <text>
    <body>
      <div n="1" type="巻">
        <div n="1" type="章段">
          <p>
            <app>
              <lem wit="#大島本">いづれの御時にか</lem>
              <rdg wit="#河内本">いづれの御ときにか</rdg>
            </app>
          </p>
        </div>
      </div>
    </body>
  </text>
</TEI>

<app>要素が校異情報を、<lem>が底本の読みを、<rdg>が異本の読みをそれぞれ示しています。

コレクション構造

校異源氏物語は全54帖(巻)から構成されており、DTS APIにおけるコレクション階層は以下のようになります:

DTS APIの実装

技術スタック

DTS APIは以下の技術スタックで実装しています:

  • ランタイム: Node.js + Express
  • ホスティング: Vercel(サーバレス関数)
  • データ: GitHub上で公開しているTEI/XMLファイル

Collectionエンドポイントの実装

Collectionエンドポイントは、コレクション情報をJSON-LD形式で返却します。

// /api/dts/collection
app.get('/api/dts/collection', (req, res) => {
  const id = req.query.id;

  if (!id) {
    // ルートコレクションの返却
    return res.json({
      "@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json",
      "@type": "Collection",
      "@id": "https://example.com/api/dts/collection",
      "title": "校異源氏物語テキストDB",
      "totalItems": 54,
      "member": [
        {
          "@type": "Resource",
          "@id": "kiritsubo",
          "title": "桐壺",
          "totalItems": 0
        }
        // ... 他の巻
      ]
    });
  }

  // 個別のリソース情報を返却
  const resource = getResourceById(id);
  return res.json(resource);
});

Navigationエンドポイントは、TEI/XMLの内部構造に基づいてテキストの参照構造を返却します。

// /api/dts/navigation
app.get('/api/dts/navigation', (req, res) => {
  const id = req.query.id;
  const level = req.query.level || 1;

  const navigation = {
    "@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json",
    "@id": `https://example.com/api/dts/navigation?id=${id}`,
    "resource": {
      "@id": id,
      "@type": "Resource"
    },
    "member": getNavigationMembers(id, level)
  };

  return res.json(navigation);
});

Documentエンドポイントの実装

Documentエンドポイントは、指定された範囲のTEI/XMLテキストを返却します。

// /api/dts/document
app.get('/api/dts/document', (req, res) => {
  const id = req.query.id;
  const ref = req.query.ref;

  // TEI/XMLファイルを読み込み
  const teiContent = loadTEIFile(id);

  if (ref) {
    // 特定の章段を抽出
    const section = extractSection(teiContent, ref);
    res.set('Content-Type', 'application/xml');
    return res.send(section);
  }

  // 全体を返却
  res.set('Content-Type', 'application/xml');
  return res.send(teiContent);
});

Vercelへのデプロイ

プロジェクト構成

dts-aadvpppaeaiitrc//ackd/teatelgsi.e//j.cndksjoaoioslvcrnoliuinegmtcaesttnuiitboo.onnj...sxjjmssl

vercel.jsonの設定

ExpressアプリケーションをVercelのサーバレス関数として動作させるための設定です。

{
  "version": 2,
  "builds": [
    {
      "src": "api/**/*.js",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/api/dts/(.*)",
      "dest": "/api/dts/$1"
    }
  ],
  "headers": [
    {
      "source": "/api/(.*)",
      "headers": [
        {
          "key": "Access-Control-Allow-Origin",
          "value": "*"
        },
        {
          "key": "Access-Control-Allow-Methods",
          "value": "GET, OPTIONS"
        }
      ]
    }
  ]
}

CORS対応

Vercelにデプロイしたexpressアプリケーションでは、vercel.jsonでCORS対応を行う必要があります。上記のheadersセクションにより、クロスオリジンのリクエストに対応しています。

まとめ

本章では、校異源氏物語テキストDBに対するDTS APIの設計と実装を解説しました。DTSの3つのエンドポイント(Collection、Navigation、Document)を通じて、TEI/XMLで符号化された校異情報に統一的なアクセス手段を提供しています。次章では、このDTS APIを利用したビューアの開発について解説します。

関連記事