class TidyJson::Serializer

A purpose-built JSON generator.

@api private

Public Class Methods

serialize(obj, json_hash) click to toggle source

Searches obj to a maximum depth of 2 for readable attributes, storing them as key-value pairs in json_hash.

@param obj [Object] A Ruby object that can be parsed as JSON. @param json_hash [{String,Symbol => to_s}] Accumulator. @return [{String => to_s}] A hash mapping of obj's visible attributes.

# File lib/tidy_json/serializer.rb, line 16
def self.serialize(obj, json_hash)
  obj.instance_variables.each do |m|
    key = m.to_s[/[^\@]\w*/].to_sym

    next unless key && !key.eql?('')

    begin
      val = obj.send(key) # assuming readable attributes . . .
    rescue NoMethodError # . . . which may not be always be the case !
      json_hash[key] = nil
    end

    begin
      # process class members of Hash type
      if val.instance_of?(Hash)
        nested_key = ''
        nested = nil

        val.each.any? do |k, v|
          unless v.instance_variables.empty?
            nested_key = k
            nested = v
          end
        end

        json_hash[key] = val

        if nested
          pos = val.keys.select { |k| k === nested_key }.first.to_sym
          nested.instance_variables.each do
            json_hash[key][pos] = serialize(nested,
                                            class: nested.class.name)
          end
        end

      # process class members of Array type
      elsif val.instance_of?(Array)
        json_hash[key] = []

        val.each do |elem|
          i = val.index(elem)

          # member is a multi-dimensional collection
          if elem.respond_to?(:each)
            nested = []
            elem.each do |e|
              j = if elem.respond_to?(:key)
                    elem.key(e)
                  else elem.index(e)
                  end

              # nested element is a class object
              if !e.instance_variables.empty?
                json_hash[key][j] = { class: e.class.name }

                # recur over the contained object
                serialize(e, json_hash[key][j])

              # some kind of collection?
              elsif e.respond_to?(:each)
                temp = []
                e.each do |el|
                  temp << if el.instance_variables.empty? then el
                          else JSON.parse(el.stringify)
                          end
                end

                nested << temp

              # scalar type
              else nested << e
              end
            end
            # ~iteration of nested array elements

            json_hash[key] << nested

          # member is a flat array
          elsif !elem.instance_variables.empty? # class object?
            json_hash[key] << { class: elem.class.name }
            serialize(elem, json_hash[key][i])

          # scalar type
          else json_hash[key] << elem
          end
        end
        # ~iteration of top-level array elements

      end
    rescue NoMethodError
      # we expected an array to behave like a hash, or vice-versa
      json_hash.store(key, val) # a shallow copy is better than nothing
    end
  end
  # ~iteration of instance variables

  json_hash
end