public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Extract XmlMini from XmlSimple. [#1474 state:committed]

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

Comments

wycats Sun Nov 30 14:01:42 -0800 2008

Too bad you’re still using REXML :(

cpjolicoeur Sun Nov 30 15:32:28 -0800 2008

ditto

NZKoz Mon Dec 01 13:13:50 -0800 2008

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