当我用Rails中的Hash.from_xml(@xml)
将XML结构转换为散列时,解析器不区分空数组和nil值,而XML将立即以终止的节点描述为空数组,例如
<audio_languages/>
与那些具有属性nil="true"
的节点解释为nil值。
XML结构(我可以控制如何生成)看起来像这样:
<response>
<medias>
<media>
<id>1</id>
<name>Media-1</name>
<audio_languages/>
<avg_rating nil="true"></avg_rating>
</media>
<media>
<id>2</id>
<name>Media-2</name>
<audio_languages/>
<avg_rating nil="true"></avg_rating>
</media>
</medias>
</response>
Hash.from_xml(@xml)
的预期输出将是:
{"response"=>{"medias"=>{"media"=>[{"id"=>"1", "name"=>"Media-1", "audio_languages"=>[], "avg_rating"=>nil}, {"id"=>"2", "name"=>"Media-2", "audio_languages"=>[], "avg_rating"=>nil}]}}}
相反,我得到audio_languages
和avg_rating
的nil值:
{"response"=>{"medias"=>{"media"=>[{"id"=>"1", "name"=>"Media-1", "audio_languages"=>nil, "avg_rating"=>nil}, {"id"=>"2", "name"=>"Media-2", "audio_languages"=>nil, "avg_rating"=>nil}]}}}
我最终使用libxml
解析节点,我正在检查节点是否具有我正在寻找的签名,以便弄清楚我是否想转换为空数组与nil值。
# Usage: Hash.from_xml_with_libxml(xml)
require 'xml/libxml'
# adapted from
# http://movesonrails.com/articles/2008/02/25/libxml-for-active-resource-2-0
class Hash
class << self
def from_xml_with_libxml(xml, strict=true)
LibXML::XML.default_load_external_dtd = false
LibXML::XML.default_pedantic_parser = strict
result = LibXML::XML::Parser.string(xml).parse
return { result.root.name.to_s => xml_node_to_hash_with_libxml(result.root)}
end
def xml_node_to_hash_with_libxml(node)
# If we are at the root of the document, start the hash
if node.element?
if node.children?
result_hash = {}
node.each_child do |child|
result = xml_node_to_hash_with_libxml(child)
if child.name == "text"
if !child.next? and !child.prev?
return result
end
elsif result_hash[child.name]
if result_hash[child.name].is_a?(Object::Array)
result_hash[child.name] << result
else
result_hash[child.name] = [result_hash[child.name]] << result
end
else
result_hash[child.name] = result
end
end
return result_hash
else
# Nodes of sort <audio_languages/>, are arrays,
# and nodes like <average_rating "nil"="true"/> are nil values.
if node.to_s.match(/^<(.+)/>$/) && nil == node.attributes["nil"]
return []
end
return nil
end
else
return node.content.to_s
end
end
end
end