Skip to content
This repository
Browse code

Extract XmlMini from XmlSimple. [#1474 state:committed]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
  • Loading branch information...
commit fea8d9d06ffaf85eb9e590ae3ac7cf082ad0c420 1 parent 8d2ca7d
Joseph Anthony Pasquale Holsten authored November 25, 2008 jeremy committed November 25, 2008
127  activesupport/lib/active_support/core_ext/hash/conversions.rb
... ...
@@ -1,23 +1,116 @@
1 1
 require 'date'
2 2
 
3  
-# Locked down XmlSimple#xml_in_string
4  
-class XmlSimple
5  
-  # Same as xml_in but doesn't try to smartly shoot itself in the foot.
6  
-  def xml_in_string(string, options = nil)
7  
-    handle_options('in', options)
  3
+# = XmlMini
  4
+# This is a derivitive work of XmlSimple 1.0.11
  5
+# Author::    Joseph Holsten <joseph@josephholsten.com>
  6
+# Copyright:: Copyright (c) 2008 Joseph Holsten
  7
+# Copyright:: Copyright (c) 2003-2006 Maik Schmidt <contact@maik-schmidt.de>
  8
+# License::   Distributes under the same terms as Ruby.
  9
+class XmlMini
  10
+  require 'rexml/document'
  11
+  include REXML
8 12
 
9  
-    @doc = parse(string)
10  
-    result = collapse(@doc.root)
  13
+  CONTENT_KEY = '__content__'
11 14
 
12  
-    if @options['keeproot']
13  
-      merge({}, @doc.root.name, result)
  15
+  # Parse an XML Document string into a simple hash
  16
+  #
  17
+  # Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
  18
+  # and uses the defaults from ActiveSupport
  19
+  #
  20
+  # string::
  21
+  #   XML Document string to parse
  22
+  #
  23
+  def self.parse(string)
  24
+    doc = REXML::Document.new(string)
  25
+    merge_element!({}, doc.root)
  26
+  end
  27
+
  28
+  private
  29
+  # Convert an XML element and merge into the hash
  30
+  #
  31
+  # hash::
  32
+  #   Hash to merge the converted element into.
  33
+  # element::
  34
+  #   XML element to merge into hash
  35
+  def self.merge_element!(hash, element)
  36
+    merge!(hash, element.name, collapse(element))
  37
+  end
  38
+
  39
+  # Actually converts an XML document element into a data structure.
  40
+  #
  41
+  # element::
  42
+  #   The document element to be collapsed.
  43
+  def self.collapse(element)
  44
+    hash = get_attributes(element)
  45
+
  46
+    if element.has_elements?
  47
+      element.each_element {|child| merge_element!(hash, child) }
  48
+      merge_texts!(hash, element) unless empty_content?(element)
14 49
     else
15  
-      result
  50
+      return merge_texts!(hash, element)
16 51
     end
  52
+    hash
17 53
   end
18 54
 
19  
-  def self.xml_in_string(string, options = nil)
20  
-    new.xml_in_string(string, options)
  55
+  # Merge all the texts of an element into the hash
  56
+  #
  57
+  # hash::
  58
+  #   Hash to add the converted emement to.
  59
+  # element::
  60
+  #   XML element whose texts are to me merged into the hash
  61
+  def self.merge_texts!(hash, element)
  62
+    unless element.has_text?
  63
+      hash
  64
+    else
  65
+      # must use value to prevent double-escaping
  66
+      text_values = element.texts.map {|t| t.value }
  67
+      merge!(hash, CONTENT_KEY, text_values.join)
  68
+    end
  69
+  end
  70
+
  71
+  # Adds a new key/value pair to an existing Hash. If the key to be added
  72
+  # already exists and the existing value associated with key is not
  73
+  # an Array, it will be wrapped in an Array. Then the new value is
  74
+  # appended to that Array.
  75
+  #
  76
+  # hash::
  77
+  #   Hash to add key/value pair to.
  78
+  # key::
  79
+  #   Key to be added.
  80
+  # value::
  81
+  #   Value to be associated with key.
  82
+  def self.merge!(hash, key, value)
  83
+    if hash.has_key?(key)
  84
+      if hash[key].instance_of?(Array)
  85
+        hash[key] << value
  86
+      else
  87
+        hash[key] = [ hash[key], value ]
  88
+      end
  89
+    elsif value.instance_of?(Array)
  90
+      hash[key] = [ value ]
  91
+    else
  92
+      hash[key] = value
  93
+    end
  94
+    hash
  95
+  end
  96
+
  97
+  # Converts the attributes array of an XML element into a hash.
  98
+  # Returns an empty Hash if node has no attributes.
  99
+  #
  100
+  # element::
  101
+  #   XML element to extract attributes from.
  102
+  def self.get_attributes(element)
  103
+    attributes = {}
  104
+    element.attributes.each { |n,v| attributes[n] = v }
  105
+    attributes
  106
+  end
  107
+
  108
+  # Determines if a document element has text content
  109
+  #
  110
+  # element::
  111
+  #   XML element to be checked.
  112
+  def self.empty_content?(element)
  113
+    element.texts.join.strip.empty?
21 114
   end
22 115
 end
23 116
 
@@ -166,15 +259,7 @@ def to_xml(options = {})
166 259
 
167 260
         module ClassMethods
168 261
           def from_xml(xml)
169  
-            require 'xmlsimple'
170  
-
171  
-            # TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
172  
-            typecast_xml_value(undasherize_keys(XmlSimple.xml_in_string(xml,
173  
-              'forcearray'   => false,
174  
-              'forcecontent' => true,
175  
-              'keeproot'     => true,
176  
-              'contentkey'   => '__content__')
177  
-            ))
  262
+            typecast_xml_value(undasherize_keys(XmlMini.parse(xml)))
178 263
           end
179 264
 
180 265
           private

3 notes on commit fea8d9d

Yehuda Katz
Owner

Too bad you’re still using REXML :(

Michael Koziarski
Owner

We’d be more than happy to take patches to use alternative reliable parsers guys.

Please sign in to comment.
Something went wrong with that request. Please try again.