はじめに

本章では、校異源氏物語テキストDBのDTS APIとDTSビューアに対して行った更新・改善について解説します。初期実装からの発展として、dts:wrapperの導入、JSON-LDへの対応強化、CTSとの互換性維持、そしてテキストDB自体の拡充について述べます。

DTS APIの更新

dts:wrapperの導入

DTS仕様では、Documentエンドポイントが返却するTEI/XMLをラップするためのdts:wrapper要素が定義されています。これにより、テキスト本体に加えてメタデータやナビゲーション情報を同時に返却することが可能になります。

<dts:wrapper xmlns:dts="https://w3id.org/dts/api#"
             xmlns:tei="http://www.tei-c.org/ns/1.0">
  <dts:metadata>
    <dts:id>kiritsubo</dts:id>
    <dts:title>桐壺</dts:title>
    <dts:parent>genji-collection</dts:parent>
  </dts:metadata>
  <dts:content>
    <tei:TEI>
      <!-- TEI/XML本体 -->
    </tei:TEI>
  </dts:content>
  <dts:navigation>
    <dts:prev><!-- 前の章段への参照 --></dts:prev>
    <dts:next><!-- 次の章段への参照 --></dts:next>
  </dts:navigation>
</dts:wrapper>

dts:wrapperの実装

Documentエンドポイントでのdts:wrapperの実装例を示します。

// api/dts/document.js(更新版)
app.get('/api/dts/document', async (req, res) => {
  const { id, ref } = req.query;
  const format = req.query.format || 'xml';

  const teiContent = await loadTEIFile(id, ref);
  const navigation = await getNavigationContext(id, ref);

  if (format === 'wrapper') {
    // dts:wrapperで包んで返却
    const wrapped = `<?xml version="1.0" encoding="UTF-8"?>
<dts:wrapper xmlns:dts="https://w3id.org/dts/api#">
  <dts:metadata>
    <dts:id>${id}</dts:id>
    <dts:ref>${ref || ''}</dts:ref>
  </dts:metadata>
  <dts:content>
    ${teiContent}
  </dts:content>
  <dts:navigation>
    ${navigation.prev ? `<dts:prev ref="${navigation.prev}"/>` : ''}
    ${navigation.next ? `<dts:next ref="${navigation.next}"/>` : ''}
  </dts:navigation>
</dts:wrapper>`;

    res.set('Content-Type', 'application/xml');
    return res.send(wrapped);
  }

  // 通常のTEI/XMLを返却
  res.set('Content-Type', 'application/xml');
  return res.send(teiContent);
});

dts:wrapperの検索システムへの応用

dts:wrapperは、検索システムの構築においても有用です。テキスト本体と構造情報を一体的に取得できるため、検索インデックスの作成時にメタデータを保持したままテキストを処理できます。

// 検索インデックス構築スクリプト
async function buildSearchIndex() {
  const collection = await getCollection();

  for (const member of collection.member) {
    const navigation = await getNavigation(member['@id']);

    for (const section of navigation.member) {
      // dts:wrapper形式でドキュメントを取得
      const wrappedDoc = await getDocument(
        member['@id'],
        section.ref,
        'wrapper'
      );

      // メタデータとテキスト本体を分離して処理
      const metadata = parseMetadata(wrappedDoc);
      const text = parseContent(wrappedDoc);

      // 検索インデックスに登録
      await indexDocument({
        id: `${member['@id']}_${section.ref}`,
        title: metadata.title,
        volume: member['@id'],
        section: section.ref,
        text: extractPlainText(text)
      });
    }
  }
}

JSON-LDへの対応強化

Collection レスポンスのJSON-LD対応

DTS仕様に準拠したJSON-LDコンテキストを適用し、Linked Dataとしての相互運用性を強化しました。

{
  "@context": {
    "@vocab": "https://w3id.org/dts/api#",
    "dts": "https://w3id.org/dts/api#",
    "dc": "http://purl.org/dc/terms/",
    "hydra": "http://www.w3.org/ns/hydra/core#",
    "tei": "http://www.tei-c.org/ns/1.0"
  },
  "@type": "Collection",
  "@id": "https://example.com/api/dts/collection",
  "dc:title": "校異源氏物語テキストDB",
  "dc:description": "源氏物語の校異情報をTEI/XMLで符号化したデータベース",
  "dc:language": "ja",
  "hydra:totalItems": 54,
  "hydra:member": [
    {
      "@type": "Resource",
      "@id": "kiritsubo",
      "dc:title": "桐壺",
      "dc:type": "巻"
    }
  ]
}

Navigation APIのレスポンスにもJSON-LDコンテキストを適用し、階層構造の表現を改善しました。

{
  "@context": "https://distributed-text-services.github.io/specifications/context/1-alpha1.json",
  "@id": "https://example.com/api/dts/navigation?id=kiritsubo",
  "resource": {
    "@id": "kiritsubo",
    "@type": "Resource",
    "dc:title": "桐壺"
  },
  "citationTrees": [
    {
      "@type": "CitationTree",
      "maxCiteDepth": 2,
      "citeStructure": [
        {
          "citeType": "章段"
        }
      ]
    }
  ],
  "member": [
    {"ref": "1", "dc:title": "第一章段"},
    {"ref": "2", "dc:title": "第二章段"}
  ]
}

CTSとの互換性

CTS URNのサポート

DTSはCTSの後継仕様ですが、CTS URNベースのテキスト参照体系もサポートしています。校異源氏物語テキストDBでは、以下のようなCTS URN形式でのアクセスにも対応しました。

#urCnT:SctUsR:NjaLit:genji.kiritsubo:1

マッピング処理

DTS APIのリクエストパラメータとCTS URNの間の変換処理を実装しています。

function parseCtsUrn(urn) {
  // urn:cts:jaLit:genji.kiritsubo:1
  const parts = urn.split(':');
  const textGroup = parts[3].split('.');

  return {
    collection: textGroup[0],  // genji
    work: textGroup[1],         // kiritsubo
    passage: parts[4] || null   // 1
  };
}

function ctsUrnToDtsParams(urn) {
  const parsed = parseCtsUrn(urn);
  return {
    id: parsed.work,
    ref: parsed.passage
  };
}

DTSビューアの更新

ページネーション機能の改善

初期バージョンではすべての章段を一覧表示していましたが、大量の章段がある場合にUIが煩雑になる問題がありました。この問題を、ドロップダウンセレクタと前後ナビゲーションの組み合わせに変更することで解決しました。

TEI Critical Apparatus表示の改善

校異情報(<app>要素)の表示をツールチップ方式に変更し、底本の読みと異本の読みを視覚的に区別できるようにしました。

/* 校異情報のツールチップ表示 */
tei-app {
  position: relative;
  background-color: #fff3cd;
  border-bottom: 1px dashed #856404;
  cursor: help;
}

tei-app:hover::after {
  content: attr(data-readings);
  position: absolute;
  bottom: 100%;
  left: 0;
  background: #343a40;
  color: #fff;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 0.85em;
  white-space: pre-line;
  z-index: 10;
}

校異源氏物語テキストDBの更新

データの拡充

テキストDBの公開範囲を段階的に拡大し、全54帖のカバレッジを向上させました。新たに追加された帖のTEI/XMLファイルは、既存のDTS API・ビューアからシームレスにアクセスできます。

TEI/XMLマークアップの改善

専門研究者のフィードバックに基づき、以下のマークアップの改善を行いました:

  • <witness>要素による証本情報の詳細化
  • <note>要素による注釈の追加
  • <choice>要素による底本・校訂テキストの対応関係の明確化

GitHubリポジトリの管理

DTS APIのソースコードと校異源氏物語のTEI/XMLデータは、GitHubリポジトリで公開・管理しています。リポジトリの構成やコントリビューション方法については、関連記事を参照してください。

まとめ

本章では、校異源氏物語テキストDBのDTS APIとDTSビューアの更新内容を解説しました。dts:wrapperの導入による検索システムへの応用、JSON-LDによるLinked Data対応の強化、CTSとの互換性維持、そしてビューアのUI改善とテキストDBの拡充を通じて、デジタル源氏物語プロジェクトの基盤をより堅固なものにしています。

DTSは、TEI/XMLで符号化されたテキストの配信と利活用において有力な標準仕様です。本書で紹介した実装例が、同様のプロジェクトに取り組む方々の参考になれば幸いです。

関連記事