2019-09-13 追記: 逆バージョンもやってみた。
久々に再帰とか使った。
def json_to_csv(src_json_path, out_csv_path)
data = JSON.parse(IO.read(src_json_path))
@result = {}
recursive_parse('', data)
header = @result.keys.join(",")
body = @result.values.join(",")
open(out_csv_path,'w') do |f|
f.puts(header)
# 改行を空白に置換
f.puts(body.gsub("\n", " "))
end
end
def recursive_parse(chain_key, obj)
if obj.class == Hash
obj.each { |k,v| recursive_parse(chain_key + "|" + k.to_s , v )}
elsif obj.class == Array
obj.each_with_index { |v, idx| recursive_parse(chain_key + "|[" + idx.to_s + "]", v)}
else
@result[chain_key] = obj
end
end
json_to_csv('./tmp/test_data.json', './tmp/out.csv')
戻す方はなんか長くなったのでクラスにしてしまった。
# frozen_string_literal: true
# reCreate json from csv
class CsvToJson
require 'json'
EMPTY_VAR_NAME = '@empty_json_hash'
COPIED_VAR_NAME = '@copied_json_hash'
def csv_to_json(src_csv_path, out_json_path)
@empty_json_hash = {}
File.open(src_csv_path, 'r') do |f|
@header = f.gets.chomp
@bodies = f.each_line.inject([]) { |res, line| res << line.chomp }
end
header_to_json_object(@header)
@bodies.each_with_index do |body, idx|
@copied_json_hash = @empty_json_hash.dup
values_into_json_object(@header, body)
File.open(renamed_path(out_json_path, idx), 'w') do |f|
f.puts JSON.pretty_generate(@copied_json_hash)
end
end
end
private
def renamed_path(path, idx)
File.dirname(path) + "/#{idx}_" + File.basename(path)
end
def header_to_json_object(line)
object_define_list = line.split(',')
object_define_list.each { |item| recursive_parse_define_string(item, 0) }
end
def make_tree(items, var_name)
items.inject(var_name) { |res, i| res + (/\[|\]/ =~ i ? i : "[\"#{i}\"]") }
end
def suffix(target)
/\[|\]/ =~ target ? ' = []' : ' = {}'
end
def recursive_parse_define_string(item, depth)
buffer = item.split('|').reject(&:empty?)
current = make_tree(buffer[0..depth], EMPTY_VAR_NAME)
next_index = depth + 1
# element at bottom. so exit this method
return eval(current + " = ''") if next_index == buffer.length
eval(current + suffix(buffer[next_index])) unless eval(current)
recursive_parse_define_string(item, next_index)
end
def values_into_json_object(header, body)
headers = header.split(',')
bodies = body.split(',')
headers.each_with_index do |item, idx|
buffer = item.split('|').reject(&:empty?)
eval(make_tree(buffer, COPIED_VAR_NAME) + ' = ' + "\"#{bodies[idx]}\"")
end
end
end
CsvToJson.new.csv_to_json('./tmp/out.csv', './tmp/out.json')