-
Notifications
You must be signed in to change notification settings - Fork 6
/
xml_reader.rb
89 lines (76 loc) · 2.17 KB
/
xml_reader.rb
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
# frozen_string_literal: true
class DarwinCore
# USAGE: Hash.from_xml:(YOUR_XML_STRING)
# modified from
# http://stackoverflow.com/questions/1230741/
# convert-a-nokogiri-document-to-a-ruby-hash/1231297#1231297
module XmlReader
def self.from_xml(xml_io)
result = Nokogiri::XML(xml_io)
{ result.root.name.to_sym => self::Node.new(result.root).value }
end
# Node is a helper class to parse xml into hash
class Node
def initialize(node)
@node = node
@val = {}
end
def value
if @node.element?
prepare_node_element
else
prepare(@node.content.to_s)
end
end
private
def prepare_node_element
add_attributes
add_children if @node.children.size.positive?
@val
end
def prepare(data)
data.class == String && data.to_i.to_s == data ? data.to_i : data
end
def add_attributes
return if @node.attributes.empty?
@val[:attributes] = {}
@node.attributes.keys.each do |key|
add_attribute(@val[:attributes], @node.attributes[key])
end
end
def add_attribute(attributes, attribute)
attributes[attribute.name.to_sym] = prepare(attribute.value)
end
def add_children
@node.children.each do |child|
process_child(child)
end
end
def process_child(child)
value = DarwinCore::XmlReader::Node.new(child).value
if child.name == "text"
handle_text(child, value)
else
add_child_to_value(child, value)
end
end
def add_child_to_value(child, value)
if @val[child.name.to_sym]
handle_child_node(child.name.to_sym, value)
else
@val[child.name.to_sym] = prepare(value)
end
end
def handle_child_node(child, val)
if @val[child].is_a?(Object::Array)
@val[child] << prepare(val)
else
@val[child] = [@val[child], prepare(val)]
end
end
def handle_text(child, val)
@val = prepare(val) unless child.next_sibling || child.previous_sibling
end
end
end
end