Редактировать:
Эта функция, описанная ниже, теперь доступна как gem . После установки с gem install json_converter
следующий фрагмент можно использовать для создания CSV из допустимой строки или объекта JSON:
require 'json_converter'
json_converter= JsonConverter.new
# Assume json is a valid JSON string or object
csv = json_converter.generate_csv json
Оригинальный ответ:
Если ваши данные JSON относительно просты (без вложений или массивов), ответ Алекса, вероятно, является самым чистым способом решения этой проблемы.
Однако, если вам нужно необходимо учитывать массивы и вложенные объекты, я попытался перенести веб-версию такого конвертера в ruby. Его можно найти здесь . Методы, которые обрабатывают фактическую реструктуризацию данных: array_from
и flatten
.
Метод 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
Метод 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
Если в исходном JSON есть какие-либо значения null
, вам нужно преобразовать их во что-то, отличное от 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
Вот краткий пример того, как это будет использоваться:
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
И, наконец, вот несколько примеров ввода / вывода:
Введите:
{
"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": "somedude@example.com"
}
},
{
"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": "somedude@example.com"
}
}
]
}
Выход:
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,somedude@example.com
x,x,x,"","","",x,x,x,x,x,x,x,x,x,x,"",x,somedude@example.com