# 機能設計書 45-XML

## 概要

本ドキュメントは、NushellにおけるXML形式のデータ変換機能（`from xml`および`to xml`コマンド）の詳細設計を記載する。これらのコマンドは、XMLドキュメントとNushellの構造化データ間の相互変換を提供する。

### 本機能の処理概要

XML変換機能は、タグ、属性、コンテンツからなるXMLデータを、Nushellのレコード構造で表現可能にする。設定ファイル、データ交換フォーマット、Webサービスのレスポンスなど、様々なXMLソースを処理できる。

**業務上の目的・背景**：XMLはSOAP Webサービス、設定ファイル（Maven、Ant等）、ドキュメント形式（XHTML、SVG等）など、エンタープライズシステムで広く使用されている。レガシーシステムとの連携やデータ変換において、XMLの読み書きは依然として重要である。

**機能の利用シーン**：
- SOAP APIレスポンスの解析
- Maven pom.xmlやAnt build.xmlの操作
- SVGファイルの内容確認・編集
- XMLベースの設定ファイル処理
- HTML/XHTMLの構造解析

**主要な処理内容**：
1. `from xml`: XMLテキストをレコード構造に変換
2. `from xml --keep-comments`: コメントノードを保持
3. `from xml --keep-pi`: 処理命令（PI）を保持
4. `from xml --allow-dtd`: DTDを含むXMLを許可
5. `to xml`: レコード構造をXMLテキストに変換
6. `to xml --indent`: インデント付きフォーマット
7. `to xml --self-closed`: 空タグを自己終了形式で出力

**関連システム・外部連携**：roxmltreeクレート（パース）およびquick-xmlクレート（シリアライズ）を使用。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLI専用機能のため画面なし |

## 機能種別

データ変換 / フォーマット変換

## 入力仕様

### 入力パラメータ

#### from xml

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | String | Yes | XML文字列（パイプライン入力） | 有効なXML形式 |
| --keep-comments | Switch | No | コメントノードを保持 | - |
| --keep-pi | Switch | No | 処理命令を保持 | - |
| --allow-dtd | Switch | No | DTDを含むドキュメントを許可 | - |

#### to xml

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | record | Yes | 変換対象レコード（パイプライン入力） | XML構造に準拠 |
| --indent / -i | Int | No | インデント幅（スペース数） | 正の整数 |
| --partial-escape / -p | Switch | No | 必須文字のみエスケープ | - |
| --self-closed / -s | Switch | No | 空タグを自己終了形式で出力 | - |

### 入力データソース

パイプライン入力（from: String型、to: record型）

## 出力仕様

### 出力データ

#### from xml

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | record | XML構造を表すレコード |

レコード構造:
```
{
  tag: String,          # タグ名
  attrs: record,        # 属性（{属性名: 値}）
  content: list         # 子要素のリスト
}
```

#### to xml

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | String | XML形式の文字列 |

### 出力先

標準出力（パイプライン出力）。to xmlはcontent_type: "application/xml"メタデータを付与。

## 処理フロー

### 処理シーケンス

```
from xml:
1. パイプライン入力を受け取る
   └─ String型を期待
2. パースオプション設定
   └─ keep-comments, keep-pi, allow-dtd
3. roxmltreeでXMLをパース
   └─ Document構造を生成
4. ルート要素をレコードに変換
   └─ element_to_value関数
5. 子ノードを再帰処理
   └─ from_node_to_value関数

to xml:
1. パイプライン入力を受け取る
   └─ record型を期待
2. quick_xml::Writerを初期化
   └─ インデント設定を適用
3. レコードをXMLイベントに変換
   └─ write_xml_entry関数（再帰）
4. XMLテキストを生成
   └─ メタデータを付与
```

### フローチャート

```mermaid
flowchart TD
    subgraph from_xml ["from xml"]
        A1[入力: String] --> B1[ParsingInfo構築]
        B1 --> C1[roxmltree::Document::parse]
        C1 --> D1{パース成功?}
        D1 -->|No| E1[エラー詳細出力]
        D1 -->|Yes| F1[from_document_to_value]
        F1 --> G1[element_to_value]
        G1 --> H1{子ノードあり?}
        H1 -->|Yes| I1[from_node_to_value再帰]
        H1 -->|No| J1[レコード出力]
        I1 --> J1
    end

    subgraph to_xml ["to xml"]
        A2[入力: record] --> B2[Job構造体初期化]
        B2 --> C2{valid columns?}
        C2 -->|No| D2[エラー: 無効なカラム]
        C2 -->|Yes| E2[write_xml_entry]
        E2 --> F2{tag判定}
        F2 -->|!| G2[コメント出力]
        F2 -->|?xxx| H2[PI出力]
        F2 -->|通常| I2[タグ出力]
        G2 --> J2[String出力 + metadata]
        H2 --> J2
        I2 --> J2
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-45-01 | レコード構造 | XML要素は{tag, attrs, content}構造で表現 | from/to xml |
| BR-45-02 | テキストノード | テキストは{tag: null, attrs: null, content: "text"}で表現 | from xml |
| BR-45-03 | コメントノード | コメントは{tag: "!", attrs: null, content: "comment"}で表現 | from xml --keep-comments |
| BR-45-04 | 処理命令 | PIは{tag: "?name", attrs: null, content: "value"}で表現 | from xml --keep-pi |
| BR-45-05 | DTD防御 | デフォルトでDTDを禁止（DoS対策） | from xml |
| BR-45-06 | 空白トリム | テキストノードの空白はトリムされる | from xml |
| BR-45-07 | 省略可能フィールド | to xmlでは空レコード/null/空リストは省略可能 | to xml |
| BR-45-08 | トップレベル制約 | 文字列/コメント/PIはルート要素にできない | to xml |

### 計算ロジック

**カラム名定数**:
- `COLUMN_TAG_NAME`: "tag"
- `COLUMN_ATTRS_NAME`: "attrs" または "attributes"
- `COLUMN_CONTENT_NAME`: "content"

**エスケープ文字**:
- 通常モード: `<`, `>`, `&`, `"`, `'`をエスケープ
- partial-escape: `<`, `>`, `&`, `"`のみエスケープ

## データベース操作仕様

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| GenericError | パースエラー | XMLが不正 | XML形式を確認 |
| CantConvert | 変換エラー | 無効なカラム名 | tag/attrs/contentを使用 |
| CantConvert | トップレベルエラー | ルート要素がrecordでない | recordを入力 |
| NonUtf8 | エンコーディングエラー | 非UTF-8文字 | UTF-8エンコーディングを使用 |

### リトライ仕様

リトライは不要。

## トランザクション仕様

本機能はトランザクション処理を行わない。

## パフォーマンス要件

- roxmltreeはDOMパーサーのため、大きなXMLはメモリを消費する
- ノード数/属性数/名前空間数に制限あり（デフォルト値）

## セキュリティ考慮事項

- DTDを使用した指数関数的エンティティ展開（Billion Laughs）攻撃への対策として、DTDはデフォルトで禁止
- 外部エンティティ参照は処理されない

## 備考

- nu_xml_formatモジュールでカラム名定数を定義
- 属性の順序はIndexMapで保持

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

XMLノードのレコード表現を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | nu_xml_format.rs | `crates/nu-command/src/formats/nu_xml_format.rs` | COLUMN_TAG_NAME等の定数定義 |
| 1-2 | xml.rs (from) | `crates/nu-command/src/formats/from/xml.rs` | ParsingInfo構造体（96-101行目） |

**読解のコツ**: XMLの各ノード種別（要素、テキスト、コメント、PI）がどのようなレコード構造にマッピングされるかを理解する。

#### Step 2: from xmlのエントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | xml.rs | `crates/nu-command/src/formats/from/xml.rs` | FromXml構造体とrun関数（48-66行目） |

**主要処理フロー**:
1. **56-58行目**: フラグ取得（keep-comments, keep-pi, allow-dtd）
2. **208-216行目**: from_xml_string_to_value関数
3. **111-131行目**: element_to_value関数（要素→レコード変換）
4. **194-202行目**: from_node_to_value関数（ノード種別判定）

#### Step 3: to xmlのエントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | xml.rs | `crates/nu-command/src/formats/to/xml.rs` | ToXml構造体とJob構造体（14-128行目） |

**主要処理フロー**:
- **92-107行目**: run関数
- **130-146行目**: Job::run関数
- **193-266行目**: write_xml_entry関数（再帰的なXML生成）
- **276-354行目**: write_tag_like関数（タグ/コメント/PI判定）

### プログラム呼び出し階層図

```
from xml
    │
    ├─ FromXml::run
    │      └─ from_xml (218-227行目)
    │             └─ from_xml_string_to_value
    │                    ├─ roxmltree::Document::parse_with_options
    │                    └─ from_document_to_value
    │                           └─ element_to_value
    │                                  └─ from_node_to_value (再帰)
    │                                         ├─ element_to_value (Element)
    │                                         ├─ text_to_value (Text)
    │                                         ├─ comment_to_value (Comment)
    │                                         └─ processing_instruction_to_value (PI)
    │
to xml
    │
    ├─ ToXml::run
    │      └─ Job::new
    │             └─ Job::run
    │                    └─ write_xml_entry (再帰)
    │                           ├─ write_xml_text (テキスト)
    │                           └─ write_tag_like
    │                                  ├─ write_comment (tag == "!")
    │                                  ├─ write_processing_instruction (tag.starts_with("?"))
    │                                  └─ write_tag (通常タグ)
```

### データフロー図

```
[入力]               [処理]                    [出力]

from xml:
String ───▶ roxmltree::Document ───▶ from_document_to_value ───▶ record
                 │                            │
                 └── DTD, ノード制限チェック ──┘

to xml:
record ───▶ quick_xml::Writer ───▶ Event[] ───▶ String
                 │                       │
                 └── indent設定 ─────────┘
                                         └── content_type: application/xml
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| xml.rs (from) | `crates/nu-command/src/formats/from/xml.rs` | ソース | from xmlコマンド実装 |
| xml.rs (to) | `crates/nu-command/src/formats/to/xml.rs` | ソース | to xmlコマンド実装 |
| nu_xml_format.rs | `crates/nu-command/src/formats/nu_xml_format.rs` | ソース | XML形式共通定数定義 |
