module XML::Schema

Public Class Methods

validate(xml_doc) click to toggle source
# File lib/validate_xml_xsi.rb, line 26
def self.validate(xml_doc)
  xml_doc = XML::parse(xml_doc) unless xml_doc.is_a?(Nokogiri::XML::Document)
  errors = []
  ## Build an all-in-one XSD document that imports all of the separate schema locations
  xsd_doc = "<?xml version=\"1.0\"?>\n"
  xsd_doc << "<xsd:schema targetNamespace=\"http://www.w3.orig/XML/1998/namespace\"\n" \
             "            xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" \
             "            version=\"1.0\">\n"
  ## Iterate over all the elements and find any xsi:schemaLocation attributes
  ## and build a hash of all of the namespaces and locations
  schemata_by_ns = {}
  xml_doc.search('//*[@xsi:schemaLocation]').each do |elem|
    elem['xsi:schemaLocation'].scan(/(\S+)\s+(\S+)/).each do |ns_set|
      if ns_loc = schemata_by_ns[ns_set.first]
        unless ns_loc.eql?(ns_set.last)
          error "MISMATCHING XMLNS XSI: #{ns_set.first} -> #{ns_loc} VS #{ns_set.last}"
        end
      else
        schemata_by_ns[ns_set.first] = ns_set.last
      end
    end
  end
  schemata_by_ns.each do |ns_href, ns_file|
    xsd_doc << "  <xsd:import namespace=\"#{ns_href}\" schemaLocation=\"#{ns_file}\"/>\n"
  end
  xsd_doc << "</xsd:schema>\n"
  xsd = Nokogiri::XML.Schema(xsd_doc)
  xsd.errors.each do |err|
    err_msg = (/ERROR: /.match(err.message)) ? $' : err.message
    errors << XML::Validation::Error.new(:XSD, err.file, err.line, err.column, err_msg, err)
  end
  errs = xsd.validate(xml_doc)
  errs.each do |err|
    err_msg = (/ERROR: /.match(err.message)) ? $' : err.message
    fname = (err.file.nil?) ? xml_doc.filename : err.file
    errors << XML::Validation::Error.new(:XML, fname, err.line, err.column, err_msg, err)
  end
  errors
end