aboutsummaryrefslogblamecommitdiffstats
path: root/activesupport/lib/active_support/xml_mini/libxml.rb
blob: 0f7ba1918b3ae4268aced0f2113aa4dd7d9615ec (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


                                 
                    
                                

               



                                                                         

                                       
         
      

                      

          

                                                  
         




       


                             




                    
                        







                                                       


                                                                                                      








































                                                      




                                                         














































                                                                         

                                                                   
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
      
      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? || cdata?
          raise LibXML::XML::Error if hash[CONTENT_ROOT].to_s.length + content.length >= LIB_XML_LIMIT
          hash[CONTENT_ROOT] = hash[CONTENT_ROOT].to_s + 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) }

          if hash.length > 1 && hash[CONTENT_ROOT].blank?
            hash.delete(CONTENT_ROOT)
          end

          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)