概要

TEI/XMLファイルを編集する際、検証に使用するrngファイルを変更することで、使用するタグや属性を限定することができます。これにより、作業者が使用するタグに混乱しない、作成されるTEI/XMLのばらつきが軽減する、といった利点が考えられます。

rngファイルを編集する方法として、以下の記事で紹介したように、Romaを使用する方法が一般的です。

この方法はトップダウン的に使用するタグや属性を限定していく方法ですが、今回は、すでに作成済みのTEI/XMLから、生成AIを用いて、ボトムアップ的にrngファイルを作成してみます。

対象データ

校異源氏物語テキストDBで公開されている以下のXMLファイルを対象とします。

https://kouigenjimonogatari.github.io/tei/01.xml

このファイルでは、以下のtei_all.rngを使用しています。

http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng

そのため、以下のように、挿入可能な多くのタグがサジェストされることがわかります。

生成AIによるrngファイルの作成

以下のようなプロンプトにより、対象とするXMLファイルのタグの使用状況などに応じて、rngファイルを作成します。

# XMLファイル用カスタムRNGスキーマ作成依頼

## 目的
- 作業効率向上のため、使用する要素・属性を限定したRNGスキーマを作成
- 作業者がタグ選択に迷わず、一貫した構造で編集できる環境を構築

## 提供するファイル
- ファイル名: 01.xml

## 要望
1. **現在のXMLファイルを分析**し、実際に使用されている要素・属性を特定
2. **最小限かつ必要十分な要素セット**でRNGスキーマを作成
3. **入力補完・バリデーション**が効果的に働くよう設計
4. **コメント付き**で、各要素の用途を明記

## 制約・方針
- 不要な要素は除外(例:○○、××は使用しない)
- 必須属性を明確に定義

## 期待する成果物
1. カスタムRNGファイル(.rng)
2. 簡潔な使用ガイド

結果、以下のrngファイルが作成されました。

<?xml version="1.0" encoding="UTF-8"?>
<!--
  カスタムRNGスキーマ: 校異源氏物語TEIファイル用
  作成日: 2024年
  
  【目的】
  - 作業効率向上のため、実際に使用する要素・属性のみに限定
  - 入力補完とバリデーションを効果的に機能させる
  - 作業者がタグ選択に迷わない明確な構造
-->
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
         xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0"
         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
         ns="http://www.tei-c.org/ns/1.0">

  <start>
    <ref name="TEI"/>
  </start>

  <!-- ====================
       ルート要素
       ==================== -->
  <define name="TEI">
    <element name="TEI">
      <a:documentation>TEI文書のルート要素</a:documentation>
      <ref name="teiHeader"/>
      <ref name="facsimile"/>
      <ref name="text"/>
    </element>
  </define>

  <!-- ====================
       ヘッダー部分(編集対象外)
       ==================== -->
  <define name="teiHeader">
    <element name="teiHeader">
      <a:documentation>
        メタデータ領域(編集作業では変更しない)
        - fileDesc: 書誌情報
        - encodingDesc: エンコーディング情報
      </a:documentation>
      <ref name="fileDesc"/>
      <ref name="encodingDesc"/>
    </element>
  </define>

  <!-- 書誌情報 -->
  <define name="fileDesc">
    <element name="fileDesc">
      <ref name="titleStmt"/>
      <ref name="publicationStmt"/>
      <ref name="sourceDesc"/>
    </element>
  </define>

  <!-- タイトル情報 -->
  <define name="titleStmt">
    <element name="titleStmt">
      <element name="title">
        <text/>
      </element>
      <element name="author">
        <text/>
      </element>
      <oneOrMore>
        <ref name="respStmt"/>
      </oneOrMore>
    </element>
  </define>

  <!-- 責任表示 -->
  <define name="respStmt">
    <element name="respStmt">
      <element name="resp">
        <attribute name="when">
          <a:documentation>作業日(YYYY-MM-DD形式)</a:documentation>
          <data type="date"/>
        </attribute>
        <text/>
      </element>
      <element name="name">
        <text/>
      </element>
    </element>
  </define>

  <!-- 出版情報 -->
  <define name="publicationStmt">
    <element name="publicationStmt">
      <element name="distributor">
        <text/>
      </element>
      <element name="date">
        <attribute name="when">
          <data type="date"/>
        </attribute>
        <text/>
      </element>
      <element name="availability">
        <element name="p">
          <element name="ref">
            <attribute name="target">
              <data type="anyURI"/>
            </attribute>
            <text/>
          </element>
        </element>
      </element>
    </element>
  </define>

  <!-- 原資料情報 -->
  <define name="sourceDesc">
    <element name="sourceDesc">
      <element name="bibl">
        <element name="author">
          <text/>
        </element>
        <element name="title">
          <text/>
        </element>
        <element name="publisher">
          <text/>
        </element>
      </element>
    </element>
  </define>

  <!-- エンコーディング情報 -->
  <define name="encodingDesc">
    <element name="encodingDesc">
      <element name="p">
        <mixed>
          <optional>
            <element name="ref">
              <attribute name="target">
                <data type="anyURI"/>
              </attribute>
              <text/>
            </element>
          </optional>
        </mixed>
      </element>
    </element>
  </define>

  <!-- ====================
       画像参照部分
       ==================== -->
  <define name="facsimile">
    <element name="facsimile">
      <a:documentation>
        画像情報を管理する領域
        - IIIF対応の画像URLとゾーン座標を定義
      </a:documentation>
      <ref name="surfaceGrp"/>
    </element>
  </define>

  <!-- 画像グループ -->
  <define name="surfaceGrp">
    <element name="surfaceGrp">
      <attribute name="facs">
        <a:documentation>IIIFマニフェストURL(必須)</a:documentation>
        <data type="anyURI">
          <param name="pattern">https://.*\.json</param>
        </data>
      </attribute>
      <oneOrMore>
        <ref name="surface"/>
      </oneOrMore>
    </element>
  </define>

  <!-- 個別画像情報 -->
  <define name="surface">
    <element name="surface">
      <a:documentation>1つの画像面を表す</a:documentation>
      <ref name="graphic"/>
      <oneOrMore>
        <ref name="zone"/>
      </oneOrMore>
    </element>
  </define>

  <!-- 画像リンク -->
  <define name="graphic">
    <element name="graphic">
      <attribute name="n">
        <a:documentation>IIIFキャンバスURL</a:documentation>
        <data type="anyURI"/>
      </attribute>
      <attribute name="url">
        <a:documentation>画像の直接URL</a:documentation>
        <data type="anyURI"/>
      </attribute>
      <empty/>
    </element>
  </define>

  <!-- 画像上の領域定義 -->
  <define name="zone">
    <element name="zone">
      <a:documentation>
        画像上の矩形領域を定義
        - ページやコラムの範囲を指定
      </a:documentation>
      <attribute name="xml:id">
        <a:documentation>ゾーンID(zone_XXXX形式)</a:documentation>
        <data type="ID">
          <param name="pattern">zone_\d{4}</param>
        </data>
      </attribute>
      <attribute name="lrx">
        <a:documentation>右下X座標</a:documentation>
        <data type="nonNegativeInteger"/>
      </attribute>
      <attribute name="lry">
        <a:documentation>右下Y座標</a:documentation>
        <data type="nonNegativeInteger"/>
      </attribute>
      <attribute name="ulx">
        <a:documentation>左上X座標</a:documentation>
        <data type="nonNegativeInteger"/>
      </attribute>
      <attribute name="uly">
        <a:documentation>左上Y座標</a:documentation>
        <data type="nonNegativeInteger"/>
      </attribute>
      <empty/>
    </element>
  </define>

  <!-- ====================
       テキスト本文(主要編集領域)
       ==================== -->
  <define name="text">
    <element name="text">
      <a:documentation>テキスト本文のコンテナ</a:documentation>
      <ref name="body"/>
    </element>
  </define>

  <!-- 本文 -->
  <define name="body">
    <element name="body">
      <a:documentation>本文領域</a:documentation>
      <oneOrMore>
        <ref name="p"/>
      </oneOrMore>
    </element>
  </define>

  <!-- 段落 -->
  <define name="p">
    <element name="p">
      <a:documentation>
        段落要素
        - 通常は文書全体で1つのp要素を使用
      </a:documentation>
      <ref name="textContent"/>
    </element>
  </define>

  <!-- ====================
       テキストコンテンツモデル
       ==================== -->
  <define name="textContent">
    <a:documentation>
      本文内で使用可能な要素群
    </a:documentation>
    <zeroOrMore>
      <choice>
        <text/>
        <ref name="lb"/>
        <ref name="pb"/>
        <ref name="seg"/>
      </choice>
    </zeroOrMore>
  </define>

  <!-- 改行 -->
  <define name="lb">
    <element name="lb">
      <a:documentation>
        改行マーカー
        - 原本の改行位置を示す
        - 空要素として使用
      </a:documentation>
      <empty/>
    </element>
  </define>

  <!-- ページ境界 -->
  <define name="pb">
    <element name="pb">
      <a:documentation>
        ページ境界マーカー
        - 新しいページの開始を示す
        - 必ず対応するzone要素を参照
      </a:documentation>
      <attribute name="n">
        <a:documentation>ページ番号(数値)</a:documentation>
        <data type="positiveInteger"/>
      </attribute>
      <attribute name="facs">
        <a:documentation>対応するzoneへの参照(#zone_XXXX形式)</a:documentation>
        <data type="string">
          <param name="pattern">#zone_\d{4}</param>
        </data>
      </attribute>
      <empty/>
    </element>
  </define>

  <!-- テキストセグメント(最重要要素) -->
  <define name="seg">
    <element name="seg">
      <a:documentation>
        テキストセグメント
        【重要】主要な編集対象要素
        - 1行または意味的なまとまりごとに分割
        - 各セグメントは固有のIDを持つ外部リソースに対応
        - corresp属性で外部データベースとリンク
      </a:documentation>
      <attribute name="corresp">
        <a:documentation>
          外部リソースへのリンク(必須)
          形式: https://w3id.org/kouigenjimonogatari/api/items/XXXX-XX.json
        </a:documentation>
        <data type="anyURI">
          <param name="pattern">https://w3id\.org/kouigenjimonogatari/api/items/\d{4}-\d{2}\.json</param>
        </data>
      </attribute>
      <text/>
    </element>
  </define>

</grammar>

XMLへの適用

以下のように、作成したrngファイルを適用します。

<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="01_custom.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<?xml-model href="01_custom.rng" type="application/xml"
   schematypens="http://purl.oclc.org/dsdl/schematron"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0">
   <teiHeader>
      <fileDesc>
         <titleStmt>
            <title>校異源氏物語・きりつぼ</title>
...

結果、例えば以下のように、使用可能なタグが限定して表示されます。

使用可能な属性も限定されています。

さらに属性の値についても、形式に制限が加えられていることがわかります。

VSCodeのScholarly XMLでも同様の制限が加わります。

https://marketplace.visualstudio.com/items?itemName=raffazizzi.sxml

このような作業環境の構築により、作業者ごとの成果物のばらつきが軽減すると考えられます。

まとめ

ボトムアップ的にrngファイルを作成する方法の一例として、参考になりましたら幸いです。