読者です 読者をやめる 読者になる 読者になる

RubyのYAML出力でkeyがシンボルになってしまうのを回避

CSV(TSV)をインポートしてYAMLに変換しようとした。

require 'csv'
require 'yaml'

tsv = CSV.table("import.tsv", { :col_sep => "\t" })

tsv.each do |line|
  id = line[:id]
  open("result/#{id}.yml", "w") do |f|
    f.write(YAML.dump(line.to_hash))
  end
end

すると、下記のように先頭にコロンがついてしまう。
これはRuby向けのYAML方言っぽいし、手で書くときにはそうしたくない。

---
:id: 1
:name: xxxx
:age: xx

出力のstyleを変更するオプションがないかなとググってみても、見つけたものはうまく動かなかった。

散々悩んだ挙句、あることに気づいた。既存のYAMLファイルを読み込んでそれに対して更新を行った場合はこの結果にならない。だからテンプレートとなるYAMLファイルを用意し、それに対してインポートするデータをはめ込んでいく方式だ。

---
id: ''
name: ''
age: ''
require 'csv'
require 'yaml'

tsv = CSV.table("import.tsv", { :col_sep => "\t" })

tsv.each do |line|
  tmpl = YAML.load_file("template.yml")
  tmpl.map do |key, val|
    tmpl[key] = line[key.to_sym]
  end
  id = line[:id]
  open("result/#{id}.yml", "w") do |f|
    f.write(YAML.dump(tmpl))
  end
end
---
id: 1
name: xxxx
age: xx

できた!!

ちなみに、何が違うのかデバッグしてみたところ、テンプレートから取得してきた場合は各データの型がHashではなくArrayだった。 あと、ループ内でテンプレートを読み込み直しているから遅いんだけど、まあ大した件数じゃないからいいや。cloneとかdupとか使うのかな?わからん。外側に出しても必ず各フィールドが上書きされるから大丈夫だと思うけどね。