CSVOとYAML
前回の記事(CSVOの表現力 - はじめてのフォーマット)の最後のほうで 行/列を入れ替えたCSVOデータを貼りましたが、YAMLに似ていると感じませんでしたか?
再掲

CSVOはもともと、VBAで業務ツールを作っていたときに、 設定情報をYAMLのような形式でワークシート上で管理できないか、 と考えたところから始まりました。
では、下のYAMLデータをCSVO形式に変換してみましょう。 (データはYAML Ain’t Markup Language (YAML™) Version 1.2のExample 2.27.を改変したもの)
invoice: 34843
date : 2001-01-23
bill-to:
given : Chris
family : Dumars
address:
lines:
- 458 Walkman Dr.
- Suite #292
city : Royal Oak
state : MI
postal : 48046
product:
- sku : BL394D
quantity : 4
description : Basketball
price : 450.00
- sku : BL4438H
quantity : 1
description : Super Hoop
price : 2392.00
tax : 251.42
total: 4443.52
comments:
- Late afternoon is best.
- Backup contact is Nancy
- Billsmer @ 338-4338.
スペースをタブにして貼り付けます。

値を一列にまとめます。

ネスト時の改行を無くして リストのマークを*に変えて完成です。

行/列を入れ替えたものと 出力したテキストデータも貼っておきましょう。

invoice:,date:,bill-to:,,,,,,,product:,,,,,,,,tax:,total:,comments:,, ,,given:,family:,address:,,,,,*,,,,*,,,,,,*,*,* ,,,,lines:,,city:,state:,postal:,sku:,quantity:,description:,price:,sku:,quantity:,description:,price:,,,,, ,,,,*,*,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,, 34843,2001-01-23,Chris,Dumars,458 Walkman Dr.,Suite #292,Royal Oak,MI,48046,BL394D,4,Basketball,450,BL4438H,1,Super Hoop,2392,251.42,4443.52,Late afternoon is best.,Backup contact is Nancy,Billsmer @ 338-4338.
CSVOの表現力
CSVOにはどのくらいの表現力があるのでしょうか。
JSONの公式サイトにあるJSONの例をCSVO形式に変換してみましょう。
例1
{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}
CSVO形式に変換してみましょう(CSVOはレコードのリストを表現するデータ形式なので、例1のJSONデータを1つ目のレコードとして扱った場合の結果となります)。
glossary,,,,,,,,,, title,GlossDiv,,,,,,,,, ,title,GlossList,,,,,,,, ,,GlossEntry,,,,,,,, ,,ID,SortAs,GlossTerm,Acronym,Abbrev,GlossDef,,,GlossSee ,,,,,,,para,GlossSeeAlso,, ,,,,,,,,*,*, ,,,,,,,,,, example glossary,S,SGML,SGML,Standard Generalized Markup Language,SGML,ISO 8879:1986,"A meta-markup language, used to create markup languages such as DocBook.",GML,XML,markup
見にくいですね…。
エクセルで見やすくしてみましょう。(参考:エクセルでCSVOを見やすくする - はじめてのフォーマット)

ほかの例も見ていきましょう。
例2
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
CSVO形式

例3
{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}
CSVO形式

例4
{"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500}},
{
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"}},
{
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"},
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true}}],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
CSVO形式

見えない…。
行/列を入れ替えてみましょう。

見やすくなりました。
例5
{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}
CSVO形式

こちらも行/列を入れ替えてみましょう。

さまざまなJSONデータをCSVO形式に変換してみました。
エクセルでCSVOを見やすくする
こちらで作ったデータを見やすくしてみましょう。
このようなデータです。
K1,,,K2,,, K1-1,,K1-2,*,,*, K1-1-1,K1-1-2,,K2-1,K2-2,K2-1,K2-2 ,,,,,, R1V1,R1V2,R1V3,R1V4,R1V5,R1V6,R1V7 R2V1,R2V2,R2V3,R2V4,R2V5,R2V6,R2V7
エクセルで開きます。

項目から次の項目の手前のセルまで選択し、

結合します(結合が好きではない場合は文字の横位置を「選択範囲内で中央」にしましょう)。

他の項目や下の階層の項目も結合しましょう。

縦方向も結合してしまいましょう(結合が好きではない場合は、文字の縦位置には「選択範囲内で中央」がないので、そのままにしておきましょう)。

出来ました。
データの構造がわかりやすくなりましたね。

そのままCSV形式で保存すると整形前とまったく同じデータが出力されます。
K1,,,K2,,, K1-1,,K1-2,*,,*, K1-1-1,K1-1-2,,K2-1,K2-2,K2-1,K2-2 ,,,,,, R1V1,R1V2,R1V3,R1V4,R1V5,R1V6,R1V7 R2V1,R2V2,R2V3,R2V4,R2V5,R2V6,R2V7
エクセルでCSVOデータを作る
手入力でCSVOのデータを作るならエクセルを使うとかんたんです。
CSVOの書き方はこちらを見てください。
エクセルでデータを入力します。

ファイルの種類をCSVにして保存します。

保存したファイルをメモ帳などで開いてみましょう。
K1,,,K2,,, K1-1,,K1-2,*,,*, K1-1-1,K1-1-2,,K2-1,K2-2,K2-1,K2-2 ,,,,,, R1V1,R1V2,R1V3,R1V4,R1V5,R1V6,R1V7 R2V1,R2V2,R2V3,R2V4,R2V5,R2V6,R2V7
出来ましたね。
ネストしたデータも表現できるCSV
一般的なCSVはネストしたデータをうまく表現できません。
そこでネストしたデータを自然に表現できるようにCSVを拡張したデータ形式を考えました。
このデータ形式をCSVO(CSV for Object)と仮に名付けます。
概要を見ていきましょう。
ヘッダ
CSVではヘッダは必須ではありません。
ヘッダがある場合は1行目がヘッダであり、2行目からデータがはじまります。
こんな感じですね。
(※以降のサンプルでは見やすいように適宜空白を入れています。)
K1, K2, K3 R1V1, R1V2, R1V3 R2V1, R2V2, R2V3
1行目はヘッダ行です。
このデータをJSONで表すと以下のようになります。
[
{"K1": "R1V1", "K2": "R1V2", "K3": "R1V3"},
{"K1": "R2V1", "K2": "R2V2", "K3": "R2V3"}
]
このデータをCSVOでは以下のように書きます。
K1, K2, K3 R1V1, R1V2, R1V3 R2V1, R2V2, R2V3
CSVOではヘッダとデータの間には空行をはさみます。
あるいはカンマなどの区切り文字は残っていてもよいでしょう。
K1, K2, K3 ,, R1V1, R1V2, R1V3 R2V1, R2V2, R2V3
そしてヘッダは複数行になることができます。
そのことでネストが表現できるようになります。
ネストの表現
こんなデータから始めましょう。
K1, K2 V1, V2
JSONで表すとこうですね。
(以降ではすべてのサンプルにそのデータのJSON表現をつけます。)
[
{
"K1": "V1",
"K2": "V2"
}
]
K2をネストさせてみましょう。
ネストが始まったらヘッダ行を増やして次の行に移動します。
K1, K2 , , K2-1, K2-2 V1, V2-1, V2-2
[
{
"K1": "V1",
"K2": {
"K2-1": "V2-1",
"K2-2": "V2-2",
}
}
]
ネストが終わったら元の行に戻ります。
K1, K2 , , K3 , K2-1, K2-2, V1, V2-1, V2-2, V3
[
{
"K1": "V1",
"K2": {
"K2-1": "V2-1",
"K2-2": "V2-2",
},
"K3": "V3"
}
]
配列の表現
配列も表現できるようにしましょう。
配列は「*」をキーの代わりに使います。
*, *, * V1, V2, V3
[
["V1", "V2", "V3"]
]
こちらもネストできます。
K1 , , , * , , * , K1-1, K1-2, K1-1, K1-2 V1 , V2 , V3 , V4
[
{
"K1": [
{"K1-1": "V1", "K1-2": "V2"},
{"K1-1": "V3", "K1-2": "V4"}
]
}
]
これでいろんなデータが表現できますね。