使用Nokogiri提取SOAP XML指定节点返回空数组求助
Hey there, let's break down why your XPath query is returning an empty array. The #1 issue here is XML namespaces—SOAP responses rely heavily on them, and if you don't account for all the namespaces in your target nodes, Nokogiri won't be able to locate them.
Here's what's happening
Your first query /SOAP:Envelope/SOAP:Body works because you're correctly using the SOAP namespace prefix for those nodes. But nodes like <findResponse> and <service.SapEgrQosQueueStatsLogRecord> are almost certainly part of a different, custom namespace defined in your XML (look for something like xmlns:ns1="http://your.service.namespace" in the root or parent nodes).
Step-by-step solutions
1. Use the document's existing namespaces
First, extract all namespaces from your XML document—this is the most reliable approach:
# Get all namespaces defined in the XML namespaces = data.collect_namespaces # This will return a hash like: {"SOAP"=>"http://schemas.xmlsoap.org/soap/envelope/", "ns1"=>"http://your.service.namespace"} # Use the namespace hash in your XPath query result_nodes = data.xpath( '/SOAP:Envelope/SOAP:Body/ns1:findResponse/ns1:result/ns1:service.SapEgrQosQueueStatsLogRecord', namespaces )
Just replace ns1 with whatever prefix shows up in the namespaces hash for your service nodes.
2. Manually define the namespace
If you already know the namespace URI for your target nodes, you can skip extracting them and define the prefix directly:
custom_namespaces = { "SOAP" => "http://schemas.xmlsoap.org/soap/envelope/", "svc" => "http://the.namespace.used.by.your.service" # Replace with your actual URI } result_nodes = data.xpath( '/SOAP:Envelope/SOAP:Body/svc:findResponse/svc:result/svc:service.SapEgrQosQueueStatsLogRecord', custom_namespaces )
3. Emergency workaround: Ignore namespaces (not recommended)
If you need a quick fix and don't have to worry about conflicting node names across namespaces, you can use local-name() to match nodes by their name alone:
result_nodes = data.xpath( '/SOAP:Envelope/SOAP:Body/*[local-name()="findResponse"]/*[local-name()="result"]/*[local-name()="service.SapEgrQosQueueStatsLogRecord"]' )
Note: This is risky because it can match unintended nodes if there are duplicates in other namespaces. Stick to the namespace-aware methods whenever possible.
Quick check to verify
To confirm the namespace issue, run this to see all nodes under Body without filtering:
data.xpath('/SOAP:Envelope/SOAP:Body/*').each do |node| puts "Node name: #{node.name}, Namespace: #{node.namespace.href}" end
This will show you the namespace URI associated with <findResponse>, which you can then use in your XPath query.
内容的提问来源于stack exchange,提问作者Armando Quiroga

