クラウド時代のWebアプリケーション・スマートフォンアプリを開発・運用する会社です。 03-4577-8680 03-6673-4950

CSVの代替としてのODSへの移行

2018-04-18

さて、この世の中には「どうしてもダブルクリックしてExcelが開かないとダメ!絶対!!」というシチュエーションがあるかもしれません。
今回ここで書くのは、そうした場合の対応手段として「ODSどうでしょう」というお話です。

今日び、MS OfficeでもODSファイルを(ダブルクリックで)開くことができ、かつODSは(当然のことながら)1セル内に改行を含めることができます。

「CSV以上の開発コストを充てられないですよ!!」と思われるかもしれませんが、ODSの仕様上、案外増えません。

ただ一方、残念なことに、シンプルな資料が少ないのは確かです。
公式リファレンスは膨大すぎて、麓に立つ者の心を折るでしょう。

ということで「最小限これで要件を満たせる」というODSの作成方法をまとめてみます。

「最小限」なので、実際にODSファイルをバラしたものと比較するとファイルが不足していますが、不足していても開けること自体は本文最後に検証手段を記載しています。
もちろんこの手段が将来にわたって有効であり続けることは保証できませんが…。

構造

まず全体的な構造として、 .ods ファイルというのは、複数のXMLファイル(+場合によっては画像など)をzip圧縮したものです。
次のセットが1ファイル分の最小限の要素となります。

  • content.xml
  • manifest.rdf
  • meta.xml
  • mimetype
  • styles.xml
  • META-INF/manifest.xml

「ファイルいっぱいあるじゃん!!」と思うかもしれませんが、実際にデータが入るのは content.xml だけで、あとのファイルの内容は全て固定で大丈夫です。
ですのでまず、固定ファイルの内容をコピペしてどこかに置いておきましょう。

なおこれら固定ファイルの内容も、削れるだけ削ってあります。
将来にわたって有効であり続けることは保証できません。
また、もっと削れるかもしれません。

固定ファイルの内容

  • manifest.rdf
<?xml version=\"1.0\" encoding=\"utf-8\"?>
<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">
  <rdf:Description rdf:about=\"styles.xml\">
    <rdf:type rdf:resource=\"http://docs.oasis-open.org/ns/office/1.2/meta/odf#StylesFile\"/>
  </rdf:Description>
  <rdf:Description rdf:about=\"\">
    <ns0:hasPart xmlns:ns0=\"http://docs.oasis-open.org/ns/office/1.2/meta/pkg#\" rdf:resource=\"styles.xml\"/>
  </rdf:Description>
  <rdf:Description rdf:about=\"content.xml\">
    <rdf:type rdf:resource=\"http://docs.oasis-open.org/ns/office/1.2/meta/odf#ContentFile\"/>
  </rdf:Description>
  <rdf:Description rdf:about=\"\">
    <ns0:hasPart xmlns:ns0=\"http://docs.oasis-open.org/ns/office/1.2/meta/pkg#\" rdf:resource=\"content.xml\"/>
  </rdf:Description>
  <rdf:Description rdf:about=\"\">
    <rdf:type rdf:resource=\"http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Document\"/>
  </rdf:Description>
</rdf:RDF>
  • meta.xml
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<office:document-meta
 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
 xmlns:ooo=\"http://openoffice.org/2004/office\"
 xmlns:grddl=\"http://www.w3.org/2003/g/data-view#\"
 office:version=\"1.2\">
<office:meta/>
</office:document-meta>
  • mimetype

文字列末に改行が入るとエラーになるので注意してください。

application/vnd.oasis.opendocument.spreadsheet
  • styles.xml
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<office:document-styles
 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
 xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"
 xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"
 xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"
 xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"
 xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"
 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
 xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"
 xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"
 xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"
 xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"
 xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"
 xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
 xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"
 xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"
 xmlns:ooo=\"http://openoffice.org/2004/office\"
 xmlns:ooow=\"http://openoffice.org/2004/writer\"
 xmlns:oooc=\"http://openoffice.org/2004/calc\"
 xmlns:dom=\"http://www.w3.org/2001/xml-events\"
 xmlns:rpt=\"http://openoffice.org/2005/report\"
 xmlns:of=\"urn:oasis:names:tc:opendocument:xmlns:of:1.2\"
 xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"
 xmlns:grddl=\"http://www.w3.org/2003/g/data-view#\"
 xmlns:tableooo=\"http://openoffice.org/2009/table\"
 xmlns:drawooo=\"http://openoffice.org/2010/draw\"
 xmlns:calcext=\"urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0\"
 xmlns:loext=\"urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0\"
 xmlns:css3t=\"http://www.w3.org/TR/css3-text/\"
 office:version=\"1.2\">
<office:font-face-decls/>
<office:styles/>
<office:automatic-styles/>
<office:master-styles/>
</office:document-styles>
  • META-INF/manifest.xml
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\" manifest:version=\"1.2\">
 <manifest:file-entry manifest:full-path=\"/\" manifest:version=\"1.2\" manifest:media-type=\"application/vnd.oasis.opendocument.spreadsheet\"/>
 <manifest:file-entry manifest:full-path=\"content.xml\" manifest:media-type=\"text/xml\"/>
 <manifest:file-entry manifest:full-path=\"meta.xml\" manifest:media-type=\"text/xml\"/>
 <manifest:file-entry manifest:full-path=\"styles.xml\" manifest:media-type=\"text/xml\"/>
 <manifest:file-entry manifest:full-path=\"manifest.rdf\" manifest:media-type=\"application/rdf+xml\"/>
</manifest:manifest>

content.xml の構造

唯一データが入る content.xml ですが、これも構造としてはさほど複雑ではありません。
全体は次のようなパーツに分類できます。

  • ヘッダ部分
  • シート
  • フッタ部分

上記のうち、ヘッダ/フッタ部分は固定文字列なので、他のファイルと同様に静的に保存しておきます。

  • content.xml ヘッダ部分
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<office:document-content
 xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"
 xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"
 xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"
 xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"
 xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"
 xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"
 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
 xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
 xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"
 xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"
 xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"
 xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"
 xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"
 xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"
 xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
 xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"
 xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"
 xmlns:ooo=\"http://openoffice.org/2004/office\"
 xmlns:ooow=\"http://openoffice.org/2004/writer\"
 xmlns:oooc=\"http://openoffice.org/2004/calc\"
 xmlns:dom=\"http://www.w3.org/2001/xml-events\"
 xmlns:xforms=\"http://www.w3.org/2002/xforms\"
 xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
 xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
 xmlns:rpt=\"http://openoffice.org/2005/report\"
 xmlns:of=\"urn:oasis:names:tc:opendocument:xmlns:of:1.2\"
 xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"
 xmlns:grddl=\"http://www.w3.org/2003/g/data-view#\"
 xmlns:tableooo=\"http://openoffice.org/2009/table\"
 xmlns:drawooo=\"http://openoffice.org/2010/draw\"
 xmlns:calcext=\"urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0\"
 xmlns:loext=\"urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0\"
 xmlns:field=\"urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0\"
 xmlns:formx=\"urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0\"
 xmlns:css3t=\"http://www.w3.org/TR/css3-text/\"
 office:version=\"1.2\">
<office:scripts/>
<office:font-face-decls/>
<office:automatic-styles/>
<office:body>
  • content.xml フッタ部分
</office:body></office:document-content>

「シート」部分の構造

さて、ここから実際に生成する部分に入ります。
「シート」部分はさらに次のようなパーツに分かれています。

  • ヘッダ
  • 行列
  • フッタ

ヘッダ文字列はただシート名を入れるだけです。
フッタ文字列は固定です。

  • シートヘッダ
<office:spreadsheet><table:table table:name=\"シート名\">
  • シートフッタ
</table:table><table:named-expressions/></office:spreadsheet>

なお、複数シートを生成する場合は、この table 部分を繰り返します。

「行列」部分の構造

この「行列」部分が実際のデータ部となります。
ここは table という要素名の通り、HTMLのtableタグと似たような構造になっています。

  • <table:table-row></table:table-row> の間が1行となります。
  • <table:table-row> 内の <table:table-cell></table:table-cell> が1セルとなります。

<table:table-cell> には、セル内のデータを指定する属性が付けられます

  • office:value-type=\"string\"
  • calcext:value-type=\"string\"

例)

<table:table-cell office:value-type=\"string\" calcext:value-type=\"string\">

<table:table-cell></table:table-cell> の間にあるものはセル内のデータとして扱われます。
この中には、改行を含むテキストを入れることが可能です。
ただし、ただ改行を入れただけの場合、改行が(入っているものの)表示がされません。
(セルをクリックすると改行が入っていることが確認できます)

改行が表示された状態にするためには、あと一手間だけ必要で、 <table:table-cell></table:table-cell> の間に入れるテキストを、改行ごとに <text:p></text:p> で囲みます。
もちろんこれは見た目上の問題なので必須ではありません。

以上です。

簡単に試してみる

上記で挙げた文字列から、次の5ファイルを静的に用意します。
とりあえず \"~/test\" に置くものと想定します。

  • manifest.rdf
  • meta.xml
  • mimetype
  • styles.xml
  • META-INF/manifest.xml

content.xml にヘッダを書き込みます。

content.xml にダミーのデータを追記します。

<office:spreadsheet>
<table:table table:name=\"テストシート\">
<table:table-row>
<table:table-cell office:value-type=\"string\" calcext:value-type=\"string\">
<text:p>1行目</text:p><text:p>2行目</text:p>
</table:table-cell>
</table:table-row>
</table:table>
<table:named-expressions/>
</office:spreadsheet>

content.xml にフッタを追記します。

全てのファイルをzipで圧縮し、拡張子を .ods にリネームします。

zip -0 ../test.ods manifest.rdf meta.xml mimetype styles.xml META-INF/manifest.xml content.xml

トラブルシューティング

test.odsが開けない場合の原因の99%は次の3つです。
content.xml にミスがある
– lintなどで確認
mimetype ファイルに改行が入っている
– エディタが保存時に改行が入る設定になっていないかなど
– zip圧縮時に余計なファイルが入ってしまっている
– ドットで始まるファイルなど