LoginSignup
0
2

More than 3 years have passed since last update.

RubyでJSONからTSV、TSVからJSONへ変換

Last updated at Posted at 2020-10-18

はじめに

やりたいこと

TSVファイルをJSON形式に、またJSONをTSVファイルに変換

環境

Ruby 2.6.5
Mac OS 10.15.5

この記事内で使用するサンプル

meibo.json
[{"name":"john","gender":"m","age":"18"},
 {"name":"paul","gender":"m","age":"20"},
 {"name":"alice","gender":"f","age":"15"},
 {"name":"dabid","gender":"m","age":"17"},
 {"name":"jasmin","gender":"f","age":"17"}]
meibo.txt
Name    gender  age
john    m   18
paul    m   20
alice   f   15
dabid   m   17
jasmin  f   17

JSONからTSVへ変換する方法

require "json" #JSONを扱うために記述

File.open("meibo.json") do |json| #JSONファイルを開く
  File.open("meibo.txt", "w") do |txt| #出力先のTSVファイルを開く
    array = JSON.load(json) #JSONの読み込み
    column = ["name","gender","age"] #ヘッダー
    txt.puts(column.join("\t")) #ヘッダを入れる
    array.each do |line| #JSON配列の各要素をTSVファイルの各行に入れる
      attr = [line["name"],line["gender"],line["age"]]
      txt.puts(attr.join("\t"))
    end
  end
end

まずは、JSONファイルを開きます。出力先のTSVファイルも書き込みモードで開いておきます。
次に、JSONファイルを読み込んでいきます。下記のように、JSONはJSON.load([Fileオブジェクト])でJSONファイルを読み込んで、ハッシュを要素とする配列で返してくれます。

require "json"

File.open("meibo.json") do |json|
  array = JSON.load(json)
  p array
end

#=>
[{"name"=>"john", "gender"=>"m", "age"=>"18"}, {"name"=>"paul", "gender"=>"m", "age"=>"20"}, {"name"=>"alice", "gender"=>"f", "age"=>"15"}, {"name"=>"dabid", "gender"=>"m", "age"=>"17"}, {"name"=>"jasmin", "gender"=>"f", "age"=>"17"}]

返ってきた配列をeachメソッドで回し、それぞれのハッシュをTSVでの一行一行にしていくイメージです。
TSVの各行に入れていくテクニックとしては、eachで取り出した各ハッシュのバリューを、キーを直接指定するかたちで取り出し、それらを一度、配列に入れ直します。そして、joinメソッドを用いてタブ区切りで配列を結合し、その文字列をTSVのファイルの各行に挿入していきます。

出力結果は以下の通りです。

meibo.txt
name    gender  age
john    m   18
paul    m   20
alice   f   15
dabid   m   17
jasmin  f   17

ハマったところ

最初は以下のように、JSON.loadしたあとの配列をeach処理に回す際、各オブジェクトのバリューの並び順通りに、TSVへ格納していきましたが、
JSONのメンバー(キーとバリューのセット)の並び順は関係ないとのこと。

つまり{"name":"john","gender":"m","age":"18"}{"name":"john","age":"18","gender":"m"}は区別しません。

よって、今回はたまたま、JSONのメンバーの並び順通りでTSVに変換しても、問題ないですが、メンバーの並び順がバラバラのJSONだと、TSVに変換した時おかしくなってしまうことがわかりました。

なので、面倒ですが一度、ハッシュのキーを指定する形でバリューを取り出し、それをカラムの並び順通りに配列にいれてから、TSVへ入れ込むと、確実です。

require "json" 

File.open("meibo.json") do |json|
  File.open("meibo.txt", "w") do |txt|
    array = JSON.load(json)
    column = ["name","gender","age"]
    txt.puts(column.join("\t"))
    array.each do |line|
      txt.puts(line.values.join("\t")) #JSONの各オブジェクトのバリューの並び通りにTSVへ入力
    end
  end
end

TSVからJSONへ変換する方法

require "json"
require "csv"

hash_ary = []

File.open("meibo.json","w") do |json|
  CSV.foreach("meibo.txt",col_sep: "\t", headers: true) do |line|
    h = {name: line[0], gender: line[1], age: line[2]}
    hash_ary << h
  end
  JSON.dump(hash_ary,json) #to_jsonを使用
end

まず、書き込みモードでJSONを書くためのファイルを用意します。

TSVファイルはCSVクラスでforeachメソッドを使って各行をハッシュに変換していきます。変換したハッシュは一度配列(ここでは、hash_ary)にいれて、全ての行が入れ終わったあとに、JSON.dumpでハッシュからJSONへの変換を行い、ファイルに書き込みます。

出力結果は以下の通り。

meibo.json
[{"name":"john","gender":"m","age":"18"},{"name":"paul","gender":"m","age":"20"},{"name":"alice","gender":"f","age":"15"},{"name":"dabid","gender":"m","age":"17"},{"name":"jasmin","gender":"f","age":"17"}]

ハマったところ

require "json"
require "csv"

hash_ary = []

File.open("meibo.json","w") do |json|
  CSV.foreach("meibo.txt",col_sep: "\t", headers: true) do |line|
    h = {name: line[0], gender: line[1], age: line[2]}
    hash_ary << h.to_json
  end
  json << hash_ary
end

最初は、上記のように書いてましたが、このようにすると。。

meibo.json
["{\"name\":\"john\",\"gender\":\"m\",\"age\":\"18\"}", "{\"name\":\"paul\",\"gender\":\"m\",\"age\":\"20\"}", "{\"name\":\"alice\",\"gender\":\"f\",\"age\":\"15\"}", "{\"name\":\"dabid\",\"gender\":\"m\",\"age\":\"17\"}", "{\"name\":\"jasmin\",\"gender\":\"f\",\"age\":\"17\"}"]

このように"の前に\が入ってうまく変換できませんでした。

to_jsonメソッドで返ってきた文字列をそのままファイルに書き込むのではなく、JSON.dumpを使用したら、解決しました。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2