korczis/json2csv

View on GitHub
lib/json2csv/convert/convert.rb

Summary

Maintainability
A
1 hr
Test Coverage
# encoding: UTF-8

require 'csv'
require 'multi_json'
require 'pathname'
require 'pp'

require_relative '../version'

module Json2Csv
  # Apollon bootstrap module
  module Convert
    DEFAULT_OPTIONS = {
      out_path: 'out.txt',
      delimiter: ','
    }

    class << self
      def convert(paths, opts = {})
        paths = [paths] unless paths.is_a?(Array)
        paths.each do |path|
          puts "Converting #{path}"

          json = load_file(path)

          json = json[opts[:root]] if opts[:root]

          tmp_opts = { out_path: "#{path}.csv" }
          process(json, DEFAULT_OPTIONS.merge(opts).merge(tmp_opts))
        end
      end

      def get_keys(obj, prefix = nil)
        keys = obj.keys
        res = keys.map do |key|
          val = obj[key]
          sanitized_key = sanitize_key(key)
          if val.is_a?(Hash)
            full_prefix = prefix ? "#{prefix}.#{sanitized_key}" : sanitized_key
            get_keys(val, full_prefix)
          else
            if prefix && !prefix.nil?
              "#{prefix}.#{sanitized_key}"
            else
              sanitized_key
            end
          end
        end

        res.compact.flatten
      end

      def get_value(obj, path)
        segments = path.split('.')
        segments.each do |segment|
          return nil if obj.nil?
          obj = obj[segment]
        end
        obj
      end

      def load_file(path)
        # Load input file
        raw = IO.read(path)

        # Try to parse json from loaded data
        begin
          return MultiJson.load(raw)
        rescue Exception => e # rubocop:disable RescueException
          log_exception(e)
        end
        nil
      end

      def log_exception(e)
        puts 'Invalid json, see error.txt'
        File.open('error.txt', 'wt') { |f| f.write(e.to_s) }
      end

      def process(json, opts = DEFAULT_OPTIONS)
        keys = json.keys

        header = nil

        out_path = opts[:out_path]
        csv_opts = {
          col_sep: opts[:delimiter] || DEFAULT_OPTIONS[:delimiter]
        }

        # Open the CSV for write
        CSV.open(out_path, 'wt', csv_opts) do |csv|
          # Take each story - json['stories'][<ID_HERE>]
          keys.each do |key|
            obj = json[key]

            if header.nil?
              header = get_keys(obj)
              csv << ['id'] + header
            end

            # Write row to output CSV
            csv << process_row(obj, key, header)
          end
        end
      end

      def process_row(obj, id, header)
        [id] + header.map do |subkey|
          # Assing value/attribute to temp variable
          tmp = get_value(obj, subkey)

          # Make temp variable empty string if null
          tmp = '' if tmp.nil?

          # Remove all new lines - replace them with empty string
          tmp = tmp.gsub(/\n/, '') if tmp.is_a?(String)
          tmp
        end
      end

      def sanitize_key(key)
        key
      end
    end
  end
end