westernmilling/agris.rb

View on GitHub
lib/hash.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# frozen_string_literal: true

require 'nokogiri'

class Hash
  class << self
    def from_xml(xml_io)
      result = Nokogiri::XML(xml_io)

      { key_value(result.root) => xml_node_to_hash(result.root) }
    end

    protected

    def xml_node_to_hash(node)
      # If we are at the root of the document, start the hash
      return node.content.to_s unless node.element?

      result_hash = {}
      if node.attributes != {}
        attributes = {}
        node.attributes.each_key do |key|
          attributes[
            key_value(node.attributes[key])
          ] = node.attributes[key].value
        end
      end

      return attributes if node.children.empty?

      node.children.each do |child|
        result = xml_node_to_hash(child)

        if child.name == 'text'
          unless child.next_sibling || child.previous_sibling
            return result unless attributes
            result_hash[key_value(child)] = result
          end
        elsif result_hash[key_value(child)]

          if result_hash[key_value(child)].is_a?(Object::Array)
            result_hash[key_value(child)] << result
          else
            result_hash[key_value(child)] = [
              result_hash[key_value(child)]
            ] << result
          end
        else
          result_hash[key_value(child)] = result
        end
      end
      if attributes
        # Add code to remove non-data attributes e.g. xml schema, namespace
        # here, if there is a collision then node content supersets
        # attributes
        result_hash = attributes.merge(result_hash)
      end
      result_hash
    end

    def key_value(node)
      node.name
    end
  end
end