diff --git a/lib/savon/qualified_message.rb b/lib/savon/qualified_message.rb
index c34243c0..c79635ad 100644
--- a/lib/savon/qualified_message.rb
+++ b/lib/savon/qualified_message.rb
@@ -2,49 +2,50 @@
module Savon
class QualifiedMessage
-
def initialize(types, used_namespaces, key_converter)
- @types = types
+ @types = types
@used_namespaces = used_namespaces
- @key_converter = key_converter
+ @key_converter = key_converter
end
def to_hash(hash, path)
- return hash unless hash
- return hash.map { |value| to_hash(value, path) } if hash.kind_of?(Array)
- return hash.to_s unless hash.kind_of? Hash
-
- hash.inject({}) do |newhash, (key, value)|
- if key == :order!
- add_namespaces_to_values(value, path)
- newhash.merge(key => value)
+ return unless hash
+ return hash.map { |value| to_hash(value, path) } if hash.is_a?(Array)
+ return hash.to_s unless hash.is_a?(Hash)
+
+ hash.each_with_object({}) do |(key, value), newhash|
+ case key
+ when :order!
+ newhash[key] = add_namespaces_to_values(value, path)
+ when :attributes!, :content!
+ newhash[key] = to_hash(value, path)
else
- translated_key = Gyoku.xml_tag(key, :key_converter => @key_converter).to_s
- translated_key << "!" if key[-1] == "!"
- newpath = path + [translated_key]
-
- if @used_namespaces[newpath]
- newhash.merge(
- "#{@used_namespaces[newpath]}:#{translated_key}" =>
- to_hash(value, @types[newpath] ? [@types[newpath]] : newpath)
- )
+ if key.to_s =~ /!$/
+ newhash[key] = value
else
- newhash.merge(translated_key => value)
+ translated_key = translate_tag(key)
+ newkey = add_namespaces_to_values(key, path).first
+ newpath = path + [translated_key]
+ newhash[newkey] = to_hash(value, newpath)
end
end
+ newhash
end
end
private
- def add_namespaces_to_values(values, path)
- values.collect! { |value|
- camelcased_value = Gyoku.xml_tag(value, :key_converter => @key_converter)
- namespace_path = path + [camelcased_value.to_s]
- namespace = @used_namespaces[namespace_path]
- "#{namespace.blank? ? '' : namespace + ":"}#{camelcased_value}"
- }
+ def translate_tag(key)
+ Gyoku.xml_tag(key, :key_converter => @key_converter).to_s
end
+ def add_namespaces_to_values(values, path)
+ Array(values).collect do |value|
+ translated_value = translate_tag(value)
+ namespace_path = path + [translated_value]
+ namespace = @used_namespaces[namespace_path]
+ namespace.blank? ? value : "#{namespace}:#{translated_value}"
+ end
+ end
end
end
diff --git a/spec/savon/qualified_message_spec.rb b/spec/savon/qualified_message_spec.rb
index 8ec9ef8b..6a4144f3 100644
--- a/spec/savon/qualified_message_spec.rb
+++ b/spec/savon/qualified_message_spec.rb
@@ -4,15 +4,63 @@ module Savon
describe QualifiedMessage, "#to_hash" do
context "if a key ends with !" do
- it "restores the ! in a key" do
- used_namespaces = {}
- key_converter = :camelcase
- types = {}
+ let(:used_namespaces) { {} }
+ let(:key_converter) { :camelcase }
+ let(:types) { {} }
+ it "restores the ! in a key" do
message = described_class.new(types, used_namespaces, key_converter)
resulting_hash = message.to_hash({:Metal! => ""}, ["Rock"])
- expect(resulting_hash).to eq({"Metal!" => ""})
+ expect(resulting_hash).to eq({ :Metal! => "" })
+ end
+
+ it "properly handles special keys when namespaces are present" do
+ used_namespaces = {
+ %w(tns Foo) => 'ns',
+ %w(tns Foo Bar) => 'ns'
+ }
+
+ hash = {
+ :foo => {
+ :bar => {
+ :zing => 'pow'
+ },
+ :cash => {
+ :@attr1 => 'val1',
+ :content! => 'Chunky Bacon'
+ },
+ :attributes! => {
+ :bar => { :attr2 => 'val2' },
+ },
+ :"self_closing/" => '',
+ :order! => [:cash, :bar, :"self_closing/"]
+ }
+ }
+
+ good_result = {
+ "ns:Foo" => {
+ 'ns:Bar' => { :zing => "pow" },
+ :cash => {
+ :@attr1 => "val1",
+ :content! => "Chunky Bacon"
+ },
+ :attributes! => {
+ 'ns:Bar' => { :attr2 => 'val2' }
+ },
+ :"self_closing/" => '',
+ :order! => [:cash, 'ns:Bar', :"self_closing/"]
+ }
+ }
+
+ good_xml = %(Chunky Baconpow)
+
+ message = described_class.new(types, used_namespaces, key_converter)
+ resulting_hash = message.to_hash(hash, ['tns'])
+ xml = Gyoku.xml(resulting_hash, key_converter: key_converter)
+
+ expect(resulting_hash).to eq good_result
+ expect(xml).to eq good_xml
end
end