在 ruby​​ 中将 .json 转换为 .csv

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7845015/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 18:01:17  来源:igfitidea点击:

Convert .json to .csv in ruby

rubyruby-on-rails-3jsoncsv

提问by Shamith c

I want to convert .json file into .csv file using ruby. Pleases help me to do this.

我想使用 ruby​​ 将 .json 文件转换为 .csv 文件。请帮助我做到这一点。

Also propose any tool to achieve this.

还提出任何工具来实现这一点。

回答by Alex Peattie

Try something like this:

尝试这样的事情:

require 'csv'
require 'json'

csv_string = CSV.generate do |csv|
  JSON.parse(File.open("foo.json").read).each do |hash|
    csv << hash.values
  end
end

puts csv_string

回答by Mark Locklear

To actually write to file...

要实际写入文件...

require 'csv'
require 'json'

CSV.open("your_csv.csv", "w") do |csv| #open new file for write
  JSON.parse(File.open("your_json.json").read).each do |hash| #open json to parse
    csv << hash.values #write value to file
  end
end

回答by korCZis

I think the easies way to convert the JSON to CSV in ruby is using json2csv ruby gem.

我认为在 ruby​​ 中将 JSON 转换为 CSV 的简单方法是使用 json2csv ruby​​ gem。

PS: I may be biased as I am author of this.

PS:我可能有偏见,因为我是本文的作者。

json2csv github repo

json2csv github 存储库

Running the command

运行命令

json2csv convert data/sample.json

Input File

输入文件

In this particular case it will convert following json:

在这种特殊情况下,它将转换以下 json:

cat data/sample.json

{
    "12345": {
        "Firstname": "Joe",
        "Lastname": "Doe",
        "Address": {
            "Street": "#2140 Taylor Street, 94133",
            "City": "San Francisco",
            "Details": {
                "note": "Pool available"
            }
        }
    },

    "45678": {
        "Firstname": "Hyman",
        "Lastname": "Plumber",
        "Address": {
            "Street": "#111 Sutter St, 94104",
            "City": "San Francisco",
            "Details": {
                "note": "Korean Deli near to main entrance"
            }
        }
    }
}

Output

输出

cat data/sample.json.csv

id,Firstname,Lastname,Address.Street,Address.City,Address.Details.note
12345,Joe,Doe,"#2140 Taylor Street, 94133",San Francisco,Pool available
45678,Hyman,Plumber,"#111 Sutter St, 94104",San Francisco,Korean Deli near to main entrance

回答by fguillen

Based on @Alex's answerbut adding csv headersand example test.

基于@Alex 的回答,但添加了csv 标头示例 test

# utils.rb
require "csv"
module Utils
  def self.array_of_hashes_to_csv(array_of_hashes)
    CSV.generate do |csv|
      csv << array_of_hashes.first.keys
      array_of_hashes.each { |hash| csv << hash.values }
    end
  end
end

# utils_test.rb
class UtilsTest < MiniTest::Unit::TestCase
  def test_array_of_hashes_to_csv
    array_of_hashes = [
      { :key1 => "value1", :key2 => "value2" },
      { :key1 => "value3", :key2 => "value4" }
    ]
    expected_result = "key1,key2\nvalue1,value2\nvalue3,value4\n"
    assert_equal(expected_result, Utils.array_of_hashes_to_csv(array_of_hashes))
  end
end

回答by Sculper

Edit:

编辑:

This functionality described below is now available as a gem. After installing with gem install json_converter, the following snippet can be used to generate a CSV from a valid JSON string or object:

下面描述的这个功能现在可以作为gem 使用。安装后gem install json_converter,以下代码段可用于从有效的 JSON 字符串或对象生成 CSV:

require 'json_converter'
json_converter= JsonConverter.new

# Assume json is a valid JSON string or object
csv = json_converter.generate_csv json

Original Answer:

原答案:

If your JSON data is relatively simple (no nesting or arrays), Alex's answer is probably the cleanest way of handling this problem.

如果您的 JSON 数据相对简单(没有嵌套或数组),Alex 的答案可能是处理这个问题的最简洁的方法。

However, if you doneed to take arrays and nested objects into account, I've attempted to port a web versionof such a converter to ruby. It can be found here. The methods that handle the actual restructuring of data are array_fromand flatten.

但是,如果您确实需要考虑数组和嵌套对象,我已尝试将此类转换器的Web 版本移植到 ruby​​。可以在这里找到。处理实际数据重组的方法是array_fromflatten

The array_frommethod attempts to identify what a "row" of data looks like for a given dataset. It is not perfect, and you may want to tweak this part for different datasets.

array_from方法尝试确定给定数据集的数据“行”是什么样的。它并不完美,您可能需要针对不同的数据集调整此部分。

# Attempt to identify what a "row" should look like
def array_from(json)
  queue, next_item = [], json
  while !next_item.nil?

    return next_item if next_item.is_a? Array

    if next_item.is_a? Hash
      next_item.each do |k, v|
        queue.push next_item[k]
      end
    end

    next_item = queue.shift
  end

  return [json]
end

The flattenmethod recursively iterates over the JSON object(s), and generates an object that represents headers and values. If an object is nested, the header for its column will be prefixed with its parent key(s), delimited by the /character.

flatten方法递归地遍历 JSON 对象,并生成一个表示标头和值的对象。如果对象是嵌套的,则其列的标题将以其父键作为前缀,并由/字符分隔。

# The path argument is used to construct header columns for nested elements
def flatten(object, path='')
  scalars = [String, Integer, Fixnum, FalseClass, TrueClass]
  columns = {}

  if [Hash, Array].include? object.class
    object.each do |k, v|
      new_columns = flatten(v, "#{path}#{k}/") if object.class == Hash
      new_columns = flatten(k, "#{path}#{k}/") if object.class == Array
      columns = columns.merge new_columns
    end

    return columns
  elsif scalars.include? object.class
      # Remove trailing slash from path
      end_path = path[0, path.length - 1]
      columns[end_path] = object
      return columns
  else
    return {}
  end
end

If there are any nullvalues in the original JSON, you'll need to convert these to something other than nilbefore attempting the conversion - you'll generally end up with uneven rows if you don't. The nils_to_stringsmethod handles that:

如果null原始 JSON 中有任何值,则需要nil在尝试转换之前将它们转换为其他值- 如果不这样做,通常最终会得到不均匀的行。该nils_to_strings方法处理:

# Recursively convert all nil values of a hash to empty strings
def nils_to_strings(hash)
  hash.each_with_object({}) do |(k,v), object|
    case v
    when Hash
      object[k] = nils_to_strings v
    when nil
      object[k] = ''
    else
      object[k] = v
    end
  end
end

Here's a brief example of how this would be used:

这是一个如何使用它的简短示例:

json = JSON.parse(File.open('in.json').read)
in_array = array_from json
in_array.map! { |x| nils_to_strings x }

out_array = []
in_array.each do |row|
  out_array[out_array.length] = flatten row
end

headers_written = false
CSV.open('out.csv', 'w') do |csv|
  out_array.each do |row|
    csv << row.keys && headers_written = true if headers_written === false
    csv << row.values
  end
end

And finally, here's some example input/output:

最后,这是一些示例输入/输出:

Input:

输入:

{
  "Forms": [
    {
      "Form": {
        "id": "x",
        "version_id": "x",
        "name": "x",
        "category": "",
        "subcategory": null,
        "is_template": null,
        "moderation_status": "x",
        "display_status": "x",
        "use_ssl": "x",
        "modified": "x",
        "Aggregate_metadata": {
          "id": "x",
          "response_count": "x",
          "submitted_count": "x",
          "saved_count": "x",
          "unread_count": "x",
          "dropout_rate": "x",
          "average_completion_time": null,
          "is_uptodate": "x"
        }
      },
      "User": {
        "username": "[email protected]"
      }
    },
    {
      "Form": {
        "id": "x",
        "version_id": "x",
        "name": "x",
        "category": "",
        "subcategory": null,
        "is_template": null,
        "moderation_status": "x",
        "display_status": "x",
        "use_ssl": "x",
        "modified": "x",
        "Aggregate_metadata": {
          "id": "x",
          "response_count": "x",
          "submitted_count": "x",
          "saved_count": "x",
          "unread_count": "x",
          "dropout_rate": "x",
          "average_completion_time": null,
          "is_uptodate": "x"
        }
      },
      "User": {
        "username": "[email protected]"
      }
    }
  ]
}

Output:

输出:

Form/id,Form/version_id,Form/name,Form/category,Form/subcategory,Form/is_template,Form/moderation_status,Form/display_status,Form/use_ssl,Form/modified,Form/Aggregate_metadata/id,Form/Aggregate_metadata/response_count,Form/Aggregate_metadata/submitted_count,Form/Aggregate_metadata/saved_count,Form/Aggregate_metadata/unread_count,Form/Aggregate_metadata/dropout_rate,Form/Aggregate_metadata/average_completion_time,Form/Aggregate_metadata/is_uptodate,User/username
x,x,x,"","","",x,x,x,x,x,x,x,x,x,x,"",x,[email protected]
x,x,x,"","","",x,x,x,x,x,x,x,x,x,x,"",x,[email protected]

回答by David Lio

Ruby code to convert from json to csv.

将 json 转换为 csv 的 Ruby 代码。

Handles arrays by converting them to double quoted strings.

通过将数组转换为双引号字符串来处理数组。

#json-to-csv.rb
require 'json'
require 'csv'

file = File.read('./input.json')
hash = JSON.parse(file)

CSV.open('./output.csv', 'w') do |csv|
  headers = hash.first.keys
  csv << headers

  hash.each do |item|
    values = item.values
    printable_values = Array.new
    values.each do |value|

      printable_values << value.to_s.gsub(/\[|\]/,'').gsub(/"/,'\'')

    end

    csv << printable_values

  end

end

回答by Ishwaryaa Balaji

This is handle headers & nested json.

这是句柄头和嵌套的 json。

require 'csv'
require 'json'

@headers = []
file = File.open('file.json')
JSON.parse(file.read).each do |h|
  h.keys.each do |key|
    @headers << key
  end
end

uniq_headers = @headers.uniq
file = File.open('file.json')
finalrow = []

JSON.parse(file.read).each do |h|
  final = {}
  @headers.each do |key2|
    final[key2] = h[key2]
  end

  finalrow << final
end

CSV.open('output.csv' , 'w') do |csv|
  csv << uniq_headers
  finalrow.each do |deal|
    csv << deal.values
  end
end