サポンテ 勉強ノート

サポンテの勉強ノート・読書メモなどを晒します。

TSV(Excel または他の表計算ソフト)から XML コメント上の表に変換するコードジェネレータ

はじめに

 C#VB.NET で使われる XML コメントには「表」を書くことができます(まあ書いている人は見たことありませんが)。正確には「table タイプの list」でしょうか。書き方を調べてみると、噴飯ものの煩わしさです。

 先日、味をしめた方法で自動化します。

コードジェネレータ

 左の入力欄に、サンプルのように TSV 文字列をコピペして、convert ボタンをクリックしてください。右側の入力欄に生成したコードが書き出されます。

 サンプル文字列がすでに入力されているので、convert ボタンをクリックするだけで動作を確認できます。

生成されるコード例

 以下のようなコードが出力されます。

/// <list type="table">
///   <listHeader>
///     <term>Id</term>
///     <term>Name</term>
///     <term>Price</term>
///   </listHeader>
///   <item>
///     <description>1</description>
///     <description>Apple</description>
///     <description>200</description>
///   </item>
///   <item>
///     <description>2</description>
///     <description>Banana</description>
///     <description>150</description>
///   </item>
///   <item>
///     <description>3</description>
///     <description>Citrus</description>
///     <description>300</description>
///   </item>
/// </list>

ソースコード

 念のため、ソースコードも載っけておきます。JSFiddle がサービス終了しちゃうかもしれませんからね。

 それに、オフラインで使いたいという要件もあるかもしれません(その場合、CDN のところは修正してください)。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TSV (from Excel or other) to C# XML Comment</title>
    <style>
        .outer {
            display: flex;
            justify-content: center;
            align-items: center;
        }
        #app {
            margin: 0 auto;
            display: inline-block;
            text-align: left;
            height: 95%;
        }
        @media (prefers-color-scheme: dark) {
            body {
                background-color: #333;
                color: #fff;
            }
            textarea {
                background-color: #444;
                color: #fff;
            }
        }
    </style>
</head>
<body>
    <div class="outer">
        <div id="app">
            <table>
                <tr>
                    <td rowspan="3">
                        <textarea name="" id="src" cols="30" rows="10" v-model="src" placeholder="Excel などからコピペしてください"></textarea>
                    </td>
                    <td></td>
                    <td rowspan="3">
                        <textarea name="" id="result" cols="30" rows="10" v-model="result"></textarea>
                    </td>
                </tr>
                <tr>
                    <td>
                        <input type="radio" id="c" v-model="ln" value="cs"><label for="c">C#</label><br />
                        <input type="radio" id="v" v-model="ln" value="vb"><label for="v">VB</label>
                    </td>
                </tr>
                <tr>
                    <td>
                        <button v-on:click="convert">convert</button><br />
                        <button v-on:click="copyResult">copy</button>
                    </td>
                </tr>
            </table>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script>
        var app = new Vue({
            el: "#app",
            data: {
                src: "Id\tName\tPrice\n1\tApple\t200\n2\tBanana\t150\n3\tCitrus\t300",
                result: "",
                ln: "cs"
            },
            methods: {
                convert: function (e) {
                    if (this.ln === 'cs') {
                        prefix = '/// ';
                    } else if (this.ln === 'vb') {
                        prefix = '\'\'\' ';
                    }
                    
                    var lines = this.src.split("\n");
                    
                    var ret = prefix + "<list type=\"table\">\n";
                    
                    ret += prefix + this.indent(1) + "<listHeader>\n";
                    var titles = lines[0].split("\t");
                    for (let c = 0; c < titles.length; c++) {
                        ret += prefix + this.indent(2) + "<term>" + titles[c] + "</term>\n";
                    }
                    ret += prefix + this.indent(1) + "</listHeader>\n";
                    
                    for (let r = 1; r < lines.length; r++) {
                        ret += prefix + this.indent(1) + "<item>\n";
                        var cols = lines[r].split("\t");
                        for (let j = 0; j < cols.length; j++) {
                            ret += prefix + this.indent(2) + "<description>" + cols[j] + "</description>\n";
                        }
                        ret += prefix + this.indent(1) + "</item>\n";
                    }
                    
                    ret += prefix + "</list>\n";
                    
                    this.result = ret;
                },
                indent: function (depth) {
                    const s = "  ";
                    let ret = '';
                    for (let i = 0; i < depth; i++) {
                        ret += s;
                    }
                    return ret;
                },
                copyResult: function () {
                    navigator.clipboard
                        .writeText(this.result)
                        .then(() => {
                            console.log('copied!')
                        })
                        .catch(e => {
                            console.error(e)
                        });
                }
            }
        });
    </script>
</body>
</html>

おわりに

 すでに VBA などで同様のツールがあっても良さそうですが、ないということは、きっと需要なんてないんでしょうね。サポンテも書いたことないですし。