require 'libxml' # = XmlMini LibXML implementation module ActiveSupport module XmlMini_LibXML #:nodoc: extend self # Parse an XML Document string or IO into a simple hash using libxml. # data:: # XML Document string or IO to parse def parse(data) if !data.respond_to?(:read) data = StringIO.new(data || '') end LibXML::XML.default_keep_blanks = false char = data.getc if char.nil? {} else data.ungetc(char) LibXML::XML::Parser.io(data).parse.to_hash end end end end module LibXML #:nodoc: module Conversions #:nodoc: module Document #:nodoc: def to_hash root.to_hash end end module Node #:nodoc: CONTENT_ROOT = '__content__' LIB_XML_LIMIT = 30000000 # Hardcoded LibXML limit # Convert XML document to hash # # hash:: # Hash to merge the converted element into. def to_hash(hash={}) if text? raise LibXML::XML::Error if content.length >= LIB_XML_LIMIT hash[CONTENT_ROOT] = content else sub_hash = insert_name_into_hash(hash, name) attributes_to_hash(sub_hash) if array? children_array_to_hash(sub_hash) elsif yaml? children_yaml_to_hash(sub_hash) else children_to_hash(sub_hash) end end hash end protected # Insert name into hash # # hash:: # Hash to merge the converted element into. # name:: # name to to merge into hash def insert_name_into_hash(hash, name) sub_hash = {} if hash[name] if !hash[name].kind_of? Array hash[name] = [hash[name]] end hash[name] << sub_hash else hash[name] = sub_hash end sub_hash end # Insert children into hash # # hash:: # Hash to merge the children into. def children_to_hash(hash={}) each { |child| child.to_hash(hash) } attributes_to_hash(hash) hash end # Convert xml attributes to hash # # hash:: # Hash to merge the attributes into def attributes_to_hash(hash={}) each_attr { |attr| hash[attr.name] = attr.value } hash end # Convert array into hash # # hash:: # Hash to merge the array into def children_array_to_hash(hash={}) hash[child.name] = map do |child| returning({}) { |sub_hash| child.children_to_hash(sub_hash) } end hash end # Convert yaml into hash # # hash:: # Hash to merge the yaml into def children_yaml_to_hash(hash = {}) hash[CONTENT_ROOT] = content unless content.blank? hash end # Check if child is of type array def array? child? && child.next? && child.name == child.next.name end # Check if child is of type yaml def yaml? attributes.collect{|x| x.value}.include?('yaml') end end end end LibXML::XML::Document.send(:include, LibXML::Conversions::Document) LibXML::XML::Node.send(:include, LibXML::Conversions::Node)