Class: XSD::XMLParser::LibXMLParser

Inherits:
Parser show all
Defined in:
lib/xsd/xmlparser/libxmlparser.rb

Instance Attribute Summary

Attributes inherited from Parser

#charset

Instance Method Summary collapse

Methods inherited from Parser

add_factory, create_parser, factory, #initialize, #parse

Constructor Details

This class inherits a constructor from XSD::XMLParser::Parser

Instance Method Details

#do_parse(string_or_readable) ⇒ Object

Parses via XML::Reader rather than the SaxParser callbacks used before. libxml-ruby's SAX2 callback (on_start_element_ns) never surfaces an attribute's own namespace prefix to Ruby: the C extension (ruby_xml_sax2_handler.c's start_element_ns_callback) only reads an attribute's local name and value, discarding the prefix/URI slots that libxml2 itself provides per attribute. That made it impossible to recognize namespace-qualified attributes such as xsi:type, xsi:nil, and xml:lang, which SOAP4R's demarshaller depends on to pick the right Ruby type -- values silently fell back to an untyped SOAP::Mapping::Object, or stayed raw strings instead of being cast to Integer/nil/etc. This gap is still present as of libxml-ruby 6.0.0 (2026); it isn't a matter of being on an old release.

XML::Reader's per-attribute #name does return the full "prefix:local" form (confirmed against libxml-ruby 2.8.0, the version pinned for Ruby 1.9.3, through the current 3.x/6.x releases) -- matching exactly what REXML's tag_start and Ox's attr callbacks already hand back, so this mirrors their convention rather than trying to resolve namespace URIs itself.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/xsd/xmlparser/libxmlparser.rb', line 35

def do_parse(string_or_readable)
  $stderr.puts "XSD::XMLParser::LibXMLParser.do_parse" if $DEBUG
  @charset = 'utf-8'
  string = string_or_readable.respond_to?(:read) ? string_or_readable.read : string_or_readable
  reader = ::LibXML::XML::Reader.string(string)
  while reader.read
    case reader.node_type
    when ::LibXML::XML::Reader::TYPE_ELEMENT
      name = reader.name
      attrs = read_attributes(reader)
      empty = reader.empty_element?
      start_element(name, attrs)
      end_element(name) if empty
    when ::LibXML::XML::Reader::TYPE_END_ELEMENT
      end_element(reader.name)
    when ::LibXML::XML::Reader::TYPE_TEXT,
         ::LibXML::XML::Reader::TYPE_CDATA,
         ::LibXML::XML::Reader::TYPE_SIGNIFICANT_WHITESPACE
      characters(reader.value)
    end
  end
rescue ::LibXML::XML::Error => e
  raise ParseError.new(e.message)
end