diff --git a/lib/savon/qualified_message.rb b/lib/savon/qualified_message.rb index 88a3c7ad..06c30a46 100644 --- a/lib/savon/qualified_message.rb +++ b/lib/savon/qualified_message.rb @@ -14,7 +14,13 @@ def to_hash(hash, path) 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| + if hash[:order!] == :use_schema || @order_with_schema + @order_with_schema = true + ordered_keys = @used_namespaces.select { |t| t.first == path.first && t.length == 2 }.keys.collect { |k| k.last } + hash[:order!] = ordered_keys + end + + result = hash.each_with_object({}) do |(key, value), newhash| case key when :order! newhash[key] = add_namespaces_to_values(value, path) @@ -32,6 +38,8 @@ def to_hash(hash, path) end newhash end + + ordered_keys(result) end private @@ -48,5 +56,15 @@ def add_namespaces_to_values(values, path) namespace.empty? ? value : "#{namespace}:#{translated_value}" end end + + def ordered_keys(hash) + return hash unless @order_with_schema + + if order_keys = hash.delete(:order!) + present_order_keys = order_keys & hash.keys + hash[:order!] = (present_order_keys + (hash.keys - present_order_keys)).select { |key| !key.to_s.end_with?('!') } + end + hash + end end end diff --git a/spec/savon/qualified_message_spec.rb b/spec/savon/qualified_message_spec.rb index f02842c6..027fda24 100644 --- a/spec/savon/qualified_message_spec.rb +++ b/spec/savon/qualified_message_spec.rb @@ -64,6 +64,35 @@ module Savon expect(xml).to eq good_xml end + it "uses schema order when :order! is set to :use_schema" do + used_namespaces = { + %w(tns Foo) => 'ns' + } + + hash = { + :foo => { + :order! => :use_schema, + :bar => 'zing', + :cash => 'pow' + } + } + + good_result = { + "ns:Foo" => { + :order! => [:bar, :cash], + :bar => 'zing', + :cash => 'pow' + } + } + + message = described_class.new(types, used_namespaces, key_converter) + resulting_hash = message.to_hash(hash, ['tns']) + + expect(Gyoku.xml(resulting_hash, key_converter: key_converter)).to eq %(zingpow) + expect(resulting_hash).to eq good_result + + end + it "properly handles boolean false" do used_namespaces = { %w(tns Foo) => 'ns'