Class: Moxml::Adapter::HeadedOx
- Defined in:
- lib/moxml/adapter/headed_ox.rb
Overview
HeadedOx adapter - combines Ox’s fast parsing with Moxml’s XPath engine.
This adapter uses:
-
Ox for XML parsing (fast C-based parser)
-
Moxml::XPath engine for comprehensive XPath 1.0 support
Unlike the standard Ox adapter which has limited XPath support through Ox’s locate() method, HeadedOx provides full XPath 1.0 functionality including all axes, predicates, and 27 standard functions.
Class Method Summary collapse
-
.at_xpath(node, expression, namespaces = {}) ⇒ Object?
Execute XPath query and return first result.
-
.capabilities ⇒ Hash
Report adapter capabilities.
-
.parse(xml, options = {}, _context = nil) ⇒ Object
Override parse to use lazy wrapping like the Ox adapter.
-
.xpath(node, expression, namespaces = {}) ⇒ Array, Object
Execute XPath query using Moxml’s XPath engine.
-
.xpath_supported? ⇒ Boolean
Check if XPath is supported.
Methods inherited from Ox
add_child, add_next_sibling, add_previous_sibling, ancestors, assign_parents, attachments, attribute_element, attributes, cdata_content, children, comment_content, create_document, create_native_cdata, create_native_comment, create_native_declaration, create_native_doctype, create_native_element, create_native_entity_reference, create_native_namespace, create_native_processing_instruction, create_native_text, declaration_attribute, doctype_external_id, doctype_name, doctype_system_id, document, duplicate_node, entity_reference_name, get_attribute, get_attribute_value, has_declaration?, inner_text, namespace, namespace_definitions, namespace_prefix, namespace_uri, next_sibling, node_name, node_type, parent, patch_node, previous_sibling, processing_instruction_content, processing_instruction_target, remove, remove_attribute, replace, replace_children, root, sax_parse, serialize, set_attribute, set_attribute_name, set_attribute_value, set_cdata_content, set_comment_content, set_declaration_attribute, set_namespace, set_node_name, set_processing_instruction_content, set_root, set_text_content, text_content, unpatch_node, validate_single_root
Methods inherited from Base
actual_native, create_cdata, create_comment, create_declaration, create_doctype, create_document, create_element, create_entity_reference, create_namespace, create_processing_instruction, create_text, duplicate_node, entity_reference_name, has_declaration?, patch_node, prepare_for_new_document, sax_parse, sax_supported?, set_attribute_name, set_attribute_value, set_root
Methods included from XmlUtils
#encode_entities, #normalize_xml_value, #validate_comment_content, #validate_declaration_encoding, #validate_declaration_standalone, #validate_declaration_version, #validate_element_name, #validate_entity_reference_name, #validate_pi_target, #validate_prefix, #validate_uri
Class Method Details
.at_xpath(node, expression, namespaces = {}) ⇒ Object?
Execute XPath query and return first result
134 135 136 137 |
# File 'lib/moxml/adapter/headed_ox.rb', line 134 def at_xpath(node, expression, namespaces = {}) result = xpath(node, expression, namespaces) result.is_a?(Array) ? result.first : result end |
.capabilities ⇒ Hash
Report adapter capabilities
HeadedOx extends Ox’s capabilities with full XPath support through Moxml’s XPath engine
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/moxml/adapter/headed_ox.rb', line 152 def capabilities { # Core adapter capabilities parse: true, # Parsing capabilities (inherited from Ox) sax_parsing: true, namespace_aware: true, namespace_support: :partial, dtd_support: true, parsing_speed: :fast, # XPath capabilities (provided by Moxml's XPath engine) xpath_support: :full, xpath_full: true, xpath_axes: :partial, # 6 of 13 axes: child, descendant, descendant-or-self, self, attribute, parent xpath_functions: :complete, # All 27 XPath 1.0 functions xpath_predicates: true, xpath_namespaces: true, xpath_variables: true, # Serialization capabilities (inherited from Ox) namespace_serialization: true, pretty_print: true, # Known limitations schema_validation: false, xslt_support: false, } end |
.parse(xml, options = {}, _context = nil) ⇒ Object
Override parse to use lazy wrapping like the Ox adapter. Previously used DocumentBuilder (eager tree construction causing ~176K allocations per 100-element parse). Lazy parse defers wrapper creation until nodes are accessed, matching Ox adapter behavior.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/moxml/adapter/headed_ox.rb', line 32 def parse(xml, = {}, _context = nil) native_doc = begin result = ::Ox.parse(xml) # result can be either Document or Element if result.is_a?(::Ox::Document) assign_parents(result) validate_single_root(result) if [:strict] result else doc = ::Ox::Document.new doc << result assign_parents(doc) doc end rescue ::Ox::ParseError => e raise Moxml::ParseError.new( e., source: xml.is_a?(String) ? xml[0..100] : nil, ) end # Use provided context if available, otherwise create new one ctx = _context || Context.new(:headed_ox) Document.new(native_doc, ctx) end |
.xpath(node, expression, namespaces = {}) ⇒ Array, Object
Execute XPath query using Moxml’s XPath engine
This overrides the Ox adapter’s xpath method which uses locate().
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/moxml/adapter/headed_ox.rb', line 67 def xpath(node, expression, namespaces = {}) # If we receive a native node, wrap it first # Document#xpath passes @native, but our compiled XPath needs Moxml nodes unless node.is_a?(Moxml::Node) # Determine the context from the node if possible # For now, create a basic context for wrapped nodes ctx = Context.new(:headed_ox) # Wrap the native node - don't rebuild the whole document node = Moxml::Node.wrap(node, ctx) end # Parse XPath expression to AST ast = XPath::Parser.parse(expression) # Compile AST to executable Proc using class method proc = XPath::Compiler.compile_with_cache(ast, namespaces: namespaces) # Execute on the node (now guaranteed to be wrapped Moxml node) result = proc.call(node) # Return native arrays for Node#xpath to wrap, scalars directly. # The adapter contract: xpath() returns Array<native> | scalar. case result when Array # XPath engine returns wrapped Moxml::Node objects. # Extract native nodes and deduplicate by object identity. native_nodes = result.map { |n| n.is_a?(Moxml::Node) ? n.native : n } seen = {} native_nodes.select do |native| id = native.object_id if seen[id] false else seen[id] = true end end when NodeSet # NodeSet from intermediate evaluation - extract natives and deduplicate seen = {} result.to_a.map(&:native).select do |native| id = native.object_id if seen[id] false else seen[id] = true end end else # Scalar values (string, number, boolean) - return as-is result end rescue StandardError => e raise Moxml::XPathError.new( "XPath execution failed: #{e.}", expression: expression, adapter: "HeadedOx", node: node, ) end |
.xpath_supported? ⇒ Boolean
Check if XPath is supported
142 143 144 |
# File 'lib/moxml/adapter/headed_ox.rb', line 142 def xpath_supported? true end |