第8章 アプリケーションの応用例

Office Servers Spreadsheet APIの操作前後を比較する

Antenna House Office Servers Spreadsheet APIでは、Excelでファイルを開くことなくシートの分割や結合を行う機能を提供しています。シートの数が膨大になり、Excel上での処理が重くなってしまったファイルなどに有用です。

このとき、SpreadsheetMLとしてはどのように内容が変わったのでしょうか。

シートごとに分割して別ファイルにする

https://www.antenna.co.jp/xts/ex_split.htmlのページからサンプルを入手しました。

workbook.xmlとsheetの一部、その関連付けファイルを見てみましょう。

元ファイル

workbook.xml
<workbook xmlns=".../spreadsheetml/2006/main" xmlns:r=".../2006/relationships" xmlns:mc=".../markup-compatibility/2006" ...>
    ...
    <workbookPr defaultThemeVersion="166925"/>
    ...
    <xr:revisionPtr revIDLastSave="0" documentId="13_ncr:1_{0403BDEF-9A08-4147-9ABF-DFB1F685379A}"
        xr6:coauthVersionLast="47" xr6:coauthVersionMax="47"
        xr10:uidLastSave="{00000000-0000-0000-0000-000000000000}"/>
    <bookViews>
        <workbookView xWindow="-120" yWindow="-120" windowWidth="29040" windowHeight="15840"
            activeTab="4" xr2:uid="{4B99DB1C-FEF3-417D-BD2D-1BB5E047592F}"/>
    </bookViews>
    <sheets>
        <sheet name="製品A通年売上" sheetId="1" r:id="rId1"/>
        <sheet name="製品B通年売上" sheetId="2" r:id="rId2"/>
        <sheet name="製品C通年売上" sheetId="3" r:id="rId3"/>
        <sheet name="製品D通年売上" sheetId="4" r:id="rId4"/>
        <sheet name="製品E通年売上" sheetId="5" r:id="rId5"/>
    </sheets>
    <calcPr calcId="191029"/>
    ...
</workbook>

workbook.xmlに5枚のシートが登録されています。関連付けIDで参照しています。

_rels/workbook.xml.rels
<Relationships xmlns=".../package/2006/relationships">
    <Relationship Id="rId8" Type=".../relationships/sharedStrings" Target="sharedStrings.xml"/>
    <Relationship Id="rId3" Type=".../relationships/worksheet" Target="worksheets/sheet3.xml"/>
    <Relationship Id="rId7" Type=".../relationships/styles" Target="styles.xml"/>
    <Relationship Id="rId2" Type=".../relationships/worksheet" Target="worksheets/sheet2.xml"/>
    <Relationship Id="rId1" Type=".../relationships/worksheet" Target="worksheets/sheet1.xml"/>
    <Relationship Id="rId6" Type=".../relationships/theme" Target="theme/theme1.xml"/>
    <Relationship Id="rId5" Type=".../relationships/worksheet" Target="worksheets/sheet5.xml"/>
    <Relationship Id="rId4" Type=".../relationships/worksheet" Target="worksheets/sheet4.xml"/>
    <Relationship Id="rId9" Type=".../relationships/calcChain" Target="calcChain.xml"/>
</Relationships>

関連付けにそれぞれのワークシートのパスとIDがあります。

worksheets/sheet1.xml
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
    xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    ...>
    <dimension ref="A1:W27"/>
    <sheetViews>
        <sheetView workbookViewId="0">
            <selection activeCell="D22" sqref="D22"/>
        </sheetView>
    </sheetViews>
    <sheetFormatPr defaultRowHeight="18.75" .../>
    <cols>
        <col min="1" max="21" width="9" style="2"/>
        <col min="22" max="23" width="9" style="9"/>
        <col min="24" max="16384" width="9" style="2"/>
    </cols>
    <sheetData>
        <row r="1" spans="1:13" s="1" customFormat="1" ...>
            <c r="A1" s="10" t="s">
                <v>20</v>
            </c>
            <c r="B1" s="11"/>
            <c r="C1" s="12"/>
            <c r="J1" s="11"/>
            <c r="K1" s="11"/>
            <c r="L1" s="11"/>
            <c r="M1" s="11"/>
        </row>
        ...
        <row r="4" spans="1:13" s="1" customFormat="1" ...>
            <c r="A4" s="3" t="s">
                <v>7</v>
            </c>
            <c r="B4" s="4">
                <v>10</v>
            </c>
            <c r="C4" s="4">
                <f>$B$18*B4</f>
                <v>100000</v>
            </c>
            <c r="D4" s="4">
                <v>0</v>
            </c>
            <c r="E4" s="4">
                <f>$B$19*D4</f>
                <v>0</v>
            </c>
            <c r="F4" s="4">
                <v>0</v>
            </c>
            <c r="G4" s="4">
                <f>$B$20*F4</f>
                <v>0</v>
            </c>
            <c r="H4" s="4">
                <v>5</v>
            </c>
            <c r="I4" s="4">
                <f>$B$21*H4</f>
                <v>10000</v>
            </c>
            <c r="J4" s="11"/>
            <c r="K4" s="11"/>
            <c r="L4" s="11"/>
            <c r="M4" s="11"/>
        </row>
        ...
    </sheetData>
    <mergeCells count="4">
        <mergeCell ref="B2:C2"/>
        <mergeCell ref="D2:E2"/>
        <mergeCell ref="F2:G2"/>
        <mergeCell ref="H2:I2"/>
    </mergeCells>
    <phoneticPr fontId="2"/>
    <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>

ワークシートの中からsheet1.xmlを確認してみました。

sharedStrings.xml
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="150" uniqueCount="30">
    <si>
        <t>新規</t>
        <rPh sb="0" eb="2">
            <t>シンキ</t>
        </rPh>
        <phoneticPr fontId="3"/>
    </si>
    <si>
        <t>バージョンアップ</t>
        <phoneticPr fontId="3"/>
    </si>
      ...
     <si>
        <t>製品A 通年売上</t>
        <rPh sb="0" eb="2">
            <t>セイヒン</t>
        </rPh>
        <rPh sb="4" eb="8">
            <t>ツウネンウリアゲ</t>
        </rPh>
        <phoneticPr fontId="2"/>
    </si>
      ...
      <si>
        <t>製品D 通年売上</t>
        <rPh sb="0" eb="2">
            <t>セイヒン</t>
        </rPh>
        <rPh sb="4" eb="8">
            <t>ツウネンウリアゲ</t>
        </rPh>
        <phoneticPr fontId="2"/>
    </si>
    <si>
        <t>製品E 通年売上</t>
        <rPh sb="0" eb="2">
            <t>セイヒン</t>
        </rPh>
        <rPh sb="4" eb="8">
            <t>ツウネンウリアゲ</t>
        </rPh>
        <phoneticPr fontId="2"/>
    </si>
</sst>

sharedStrings.xmlには各シートのセルで使われている全ての文字列が格納されています。

シート間を跨ぐ参照を含む計算処理がある場合ではcalcChain.xmlの比較なども行いたいところですが、今回は単純な分割ですので省略しました。

操作後のファイル

Spreadsheet APIによって分割し、生成された「monthlyreport_製品A通年売上.xslx」の内容を元ファイルと比較します。

「monthlyreport_製品A通年売上」のworkbook.xml
<x:workbook ...
    xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    ...
    xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <x:fileVersion appName="xl" lastEdited="7" lowestEdited="7" rupBuild="24228"/>
    <x:workbookPr defaultThemeVersion="166925"/>
    ...
    <xr:revisionPtr revIDLastSave="0" documentId="13_ncr:1_{0403BDEF-9A08-4147-9ABF-DFB1F685379A}"
        xr6:coauthVersionLast="47" xr6:coauthVersionMax="47"
        xr10:uidLastSave="{00000000-0000-0000-0000-000000000000}"/>
    <x:sheets>
        <x:sheet name="製品A通年売上" sheetId="1" r:id="rId1"/>
    </x:sheets>
    <x:calcPr calcId="191029"/>
    ...
</x:workbook>

元のファイルではSpreadsheetMLの名前空間のプレフィックスが省略されていましたが、ツール処理時に追加されています。スクリーンショットの通り、動作には特に影響しません。

defaultThemeVersionの値も保持されています。themeパーツ側に変更がなければ元ファイルと同じテーマが適用されることになります。

mc:Ignorableに登録されている拡張である、xr名前空間の要素xr:revisionPtrは保持されています。属性の値も同じであるので、Excelで開いたときには元ファイルと同じ情報を示すはずです。

顕著な違いとして、sheets要素の子のsheet要素が1つだけになっています。シートごとに別のxlsxファイルにする操作を行ったので期待通りに動作していることが分かります。

workbook.xml.rels
<Relationships xmlns=".../package/2006/relationships">
    <Relationship Type=".../relationships/sharedStrings" Target="sharedStrings.xml" Id="rId8"/>
    <Relationship Type=".../relationships/styles" Target="styles.xml" Id="rId7"/>
    <Relationship Type=".../relationships/worksheet" Target="worksheets/sheet1.xml" Id="rId1"/>
    <Relationship Type=".../relationships/theme" Target="theme/theme1.xml" Id="rId6"/>
    <Relationship Type=".../relationships/calcChain" Target="calcChain.xml" Id="rId9"/>
</Relationships>

ワークシートの分割を行ったため、ワークシートの関連付けは期待通り「製品A通年売上」のシートであるsheet1.xmlのみが残っています。

sheet1.xml
<x:worksheet xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ...>
    <x:dimension ref="A1:W27"/>
    <x:sheetViews>
        <x:sheetView workbookViewId="0">
            <x:selection activeCell="D22" sqref="D22"/>
        </x:sheetView>
    </x:sheetViews>
    <x:sheetFormatPr defaultRowHeight="18.75" .../>
    <x:cols>
        <x:col min="1" max="21" width="9" style="2"/>
        <x:col min="22" max="23" width="9" style="9"/>
        <x:col min="24" max="16384" width="9" style="2"/>
    </x:cols>
    <x:sheetData>
        <x:row r="1" spans="1:13" s="1" customFormat="1" ...>
            <x:c r="A1" s="10" t="s">
                <x:v>0</x:v>
            </x:c>
            <x:c r="B1" s="11"/>
            <x:c r="C1" s="12"/>
            <x:c r="J1" s="11"/>
            <x:c r="K1" s="11"/>
            <x:c r="L1" s="11"/>
            <x:c r="M1" s="11"/>
        </x:row>
        ...
        <x:row r="4" spans="1:13" s="1" customFormat="1" ...>
            <x:c r="A4" s="3" t="s">
                <x:v>9</x:v>
            </x:c>
            <x:c r="B4" s="4">
                <x:v>10</x:v>
            </x:c>
            <x:c r="C4" s="4">
                <x:f>$B$18*B4</x:f>
                <x:v>100000</x:v>
            </x:c>
            <x:c r="D4" s="4">
                <x:v>0</x:v>
            </x:c>
            <x:c r="E4" s="4">
                <x:f>$B$19*D4</x:f>
                <x:v>0</x:v>
            </x:c>
            <x:c r="F4" s="4">
                <x:v>0</x:v>
            </x:c>
            <x:c r="G4" s="4">
                <x:f>$B$20*F4</x:f>
                <x:v>0</x:v>
            </x:c>
            <x:c r="H4" s="4">
                <x:v>5</x:v>
            </x:c>
            <x:c r="I4" s="4">
                <x:f>$B$21*H4</x:f>
                <x:v>10000</x:v>
            </x:c>
            <x:c r="J4" s="11"/>
            <x:c r="K4" s="11"/>
            <x:c r="L4" s="11"/>
            <x:c r="M4" s="11"/>
        </x:row>
         ...
       </x:sheetData>
    <x:mergeCells count="4">
        <x:mergeCell ref="B2:C2"/>
        <x:mergeCell ref="D2:E2"/>
        <x:mergeCell ref="F2:G2"/>
        <x:mergeCell ref="H2:I2"/>
    </x:mergeCells>
    <x:phoneticPr fontId="2"/>
    <x:pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
</x:worksheet>

元のxlsxと同じ参照のワークシートの内容を見てみました。元ファイルから大きな変更はありません。必要箇所のみを変更すれば他の箇所の変更が必要ない、OOXMLで扱うことによるパーツ化の恩恵といえるでしょう。

r="A4"、s="3"のx:c要素の値v要素を見てみましょう。元ファイルと違いますね。この場所は文字列が格納されていました。次の箇所でsharedStrings.xmlを見てみましょう。

この例ではsheet1.xmlでパーツ名に変更がありませんでしたが、パーツ名に変更があった場合も関連付けを適切に設定すれば期待通り表示されます。

sharedStrings.xml
<x:sst count="150" uniqueCount="30"
    xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <x:si>
        <x:t>製品A 通年売上>/x:t>
        <x:rPh sb="0" eb="2">
            <x:t>セイヒン</x:t>
        </x:rPh>
        <x:rPh sb="4" eb="8">
            <x:t>ツウネンウリアゲ</x:t>
        </x:rPh>
        <x:phoneticPr fontId="2"/>
    </x:si>
    <x:si>
        <x:t>新規</x:t>
        <x:rPh sb="0" eb="2">
            <x:t>シンキ</x:t>
        </x:rPh>
        <x:phoneticPr fontId="3"/>
    </x:si>
    <x:si>
        <x:t>追加</x:t>
        <x:rPh sb="0" eb="2">
            <x:t>ツイカ</x:t>
        </x:rPh>
        <x:phoneticPr fontId="3"/>
    </x:si>
</x:sst>

「製品A通算売上」のシートに登場しない「製品D 通年売上」「製品E 通年売上」などが削除されています。

「製品A通算売上」のシートに登場する文字列の順序が変わっています。sst内の要素は位置で参照されるため、sheet1.xmlで期待通りの表示を得るためにはsharedStrings.xmlでの変更を反映させる必要がありました。

ワークシートの分割を通して、関連付けによる構造の柔軟性や、XMLであるためにコンテンツ内の変更箇所がすぐに把握できることなどが分かりました。