module FHIR
module Xml
#
# This module includes methods to serialize or deserialize FHIR resources to and from XML.
#
def to_xml
hash = {}
hash = self.to_hash
hash.delete('resourceType')
doc = Nokogiri::XML::Document.new
doc.encoding = 'utf-8'
doc.root = hash_to_xml_node(self.resourceType,hash,doc)
doc.root.default_namespace = 'http://hl7.org/fhir'
doc.to_xml
end
# Hash keys are ordered in Ruby 1.9 and later, so we can rely on their order
# to be the correct order for the XML serialization.
def hash_to_xml_node(name,hash,doc)
node = Nokogiri::XML::Node.new(name,doc)
# if hash contains resourceType
# create a child node with the name==resourceType
# fill that, and place the child under the above `node`
if hash['resourceType']
child_name = hash['resourceType']
hash.delete('resourceType')
child = hash_to_xml_node(child_name, hash, doc)
node.add_child(child)
return node
end
hash.each do |key,value|
next if(['extension','modifierExtension'].include?(name) && key=='url')
next if(key == 'id' && !FHIR::RESOURCES.include?(name))
if value.is_a?(Hash)
node.add_child(hash_to_xml_node(key,value,doc))
elsif value.is_a?(Array)
value.each do |v|
if v.is_a?(Hash)
node.add_child(hash_to_xml_node(key,v,doc))
else
child = Nokogiri::XML::Node.new(key,doc)
child.set_attribute('value',v)
node.add_child(child)
end
end
else
child = Nokogiri::XML::Node.new(key,doc)
if(name=='text' && key=='div')
child.set_attribute('xmlns','http://www.w3.org/1999/xhtml')
html = value.strip
if html.start_with?('
')
html = html[html.index('>')+1..-7]
end
child.inner_html = html
else
child.set_attribute('value',value)
end
node.add_child(child)
end
end
node.set_attribute('url', hash['url']) if ['extension','modifierExtension'].include?(name)
node.set_attribute('id', hash['id']) if hash['id'] && !FHIR::RESOURCES.include?(name)
node
end
def self.from_xml(xml)
doc = Nokogiri::XML(xml)
doc.root.add_namespace_definition('f', 'http://hl7.org/fhir')
doc.root.add_namespace_definition('x', 'http://www.w3.org/1999/xhtml')
hash = xml_node_to_hash(doc.root)
resource = nil
begin
resourceType = doc.root.name
klass = Module.const_get("FHIR::#{resourceType}")
resource = klass.new(hash)
rescue Exception => e
FHIR.logger.error("Failed to deserialize XML:\n#{xml}\n#{e.backtrace}")
resource = nil
end
resource
end
def self.xml_node_to_hash(node)
hash = {}
node.children.each do |child|
next if [Nokogiri::XML::Text,Nokogiri::XML::Comment].include?(child.class)
key = child.name
if node.name=='text' && key=='div'
hash[key] = child.to_xml
else
value = child.get_attribute('value')
if value.nil? && !child.children.empty?
value = xml_node_to_hash(child)
end
if hash[key]
hash[key] = [ hash[key] ] unless hash[key].is_a?(Array)
hash[key] << value
else
hash[key] = value
end
end
end
hash['url'] = node.get_attribute('url') if ['extension','modifierExtension'].include?(node.name)
hash['id'] = node.get_attribute('id') if node.get_attribute('id') # Testscript fixture ids (applies to any BackboneElement)
hash['resourceType'] = node.name if FHIR::RESOURCES.include?(node.name)
if( # If this hash contains nothing but an embedded resource, we should return that
# embedded resource without the wrapper
hash.keys.length==1 &&
FHIR::RESOURCES.include?(hash.keys.first) &&
hash.values.first.is_a?(Hash) &&
hash.values.first['resourceType']==hash.keys.first
)
hash.values.first
else
hash
end
end
def self.is_valid?(xml)
validate(xml).empty?
end
def self.validate(xml)
defns = File.expand_path '../../definitions/schema',File.dirname(File.absolute_path(__FILE__))
schema = File.join(defns,'fhir-all.xsd')
xsd = Nokogiri::XML::Schema(File.new(schema))
xsd.validate(Nokogiri::XML(xml))
end
private :hash_to_xml_node
private_class_method :xml_node_to_hash
end
end