为什么xPath搜索在REXML中有效,而在Hpricot中无效



我使用的是Rails 3.2和Hpricot。

我想通过子元素的内容找到一个XML元素,并将其转换为Ruby对象,稍后将对其进行渲染。

换句话说,我想找到‘vehicle’元素,其中它的子‘line_number’内容等于1234

这与REXML和以下xPath:配合良好

/gsip/vehicle[line_number[text()=1234]]

REXML很慢,所以我切换到Hpricot,在那里相同的xPath找到所有车辆元素,而不仅仅是‘line_number’等于1234的元素。

为什么这能找到所有的车辆?

file_path = Rails.root.join('public','gsip','gsip-vehicle-data.xml')
q = "/gsip/vehicle[line_number[text()=#{params[:id]}]]"
@vehicle_data = { :date => Date.today - 10.years }   # initiate with very old date
xmldoc = File.read(file_path)
doc = Hpricot::XML(xmldoc)
doc.search(q) do |e|
  if e.at('line_number').innerText == params[:id]  # This line shouldn't be necessary?!
    logger.info( "#{e.at('pa_number').innerText} (#{e.at('line_number').innerText} from #{e.at('date').innerText})" )
    vehicle_date = Date.strptime(e.at('date').innerText, "%d.%m.%Y")
    #logger.info('date: ' + vehicle_date.to_s)
    if vehicle_date > @vehicle_data[:date]
      e.children.select do |n|
        logger.info("#{n.name} = #{n.innerText}")
        @vehicle_data[n.name] = n.innerText
      end
    end
  end
end

这会找到搜索到的车辆,但速度较慢:

file_path = Rails.root.join('public','gsip','gsip-vehicle-data.xml')
q = "/gsip/vehicle[line_number[text()=#{params[:id]}]]"
@vehicle_data = { :date => Date.today - 10.years }   # initiate with very old date
XPath.each(xmldoc, q ) { |e|
  #find the latest vehicle with given line_number
  vehicle_date = Date.strptime(XPath.first(e,'date').text, "%d.%m.%Y")
  if vehicle_date > @vehicle_data[:date]
    e.elements.each { |n|
      @vehicle_data[n.name] = n.text
    }
  end
}

我的XML:

<gsip export_date="7/25/2012 12:04:27 PM" schema_version="1.01">
  <vehicle id="ABC">
    <date>02.07.2012</date>
    <line_number>1234</line_number>
    <pa_number>ABC</pa_number>
    <vin>VIN</vin>
    <my>2012</my>
  </vehicle>
  <vehicle id="ABD">
    <date>02.07.2012</date>
    <line_number>8348</line_number>
    <pa_number>ABD</pa_number>
    <vin>VIN</vin>
    <my>2012</my>
  </vehicle>
  <vehicle>
  ...
  </vehicle>
  ...
</gsip>

更新

我切换到Nokogiri:

我的请求(localhost)已从4秒下降到250秒。我的XML文件是5.6MB。由于这可能对其他人有帮助,我将我的更改粘贴在下面:

class IncidentsController < ApplicationController
  require 'nokogiri'
  # ....
  def vehicle
    # helpfull links: =============================================================================
    # Some say Nokogire is best:  http://nokogiri.org/
    # recursive link: http://stackoverflow.com/questions/11665126/why-xpath-search-works-in-rexml-but-not-with-hpricot
    # =============================================================================================
    # check if PA Number or Line Number is given:
    num = ''
    if params[:id] =~ /^d{4}$/
      num = 'line_number'
    elsif params[:id] =~ /^[dw]{6}$/
      num = 'pa_number'
    elsif params[:id] =~ /^[dw]{17}$/
      num = 'vin'
    end
    # read Vehicle Data from XML File
    file_path = Rails.root.join('private','gsip','gsip-vehicle-data.xml')
    q = "/gsip/vehicle[#{num}/text()='#{params[:id]}']"
    @vehicle_data = { :date => Date.today - 10.years }   # initiate with very old date
    #logger.info("*** Find Vehicle Data in XML. xPath: #{q}")
    doc = Nokogiri::XML( File.read(file_path) )
    doc.xpath(q).each do |e|
      vehicle_date = Date.strptime(e.xpath('date').first.content, "%d.%m.%Y")
      #logger.info("Date: #{vehicle_date.to_s}")
      if vehicle_date > @vehicle_data[:date]
      e.element_children.all? do |n|
        @vehicle_data[n.name] = n.content
      end
      end
    end
    respond_to do |format|
      format.html { redirect_to connectors_path }
      format.json { render :json => @vehicle_data }
      format.xml { render :xml => @vehicle_data }
    end
  end
  # ...
end

我是Rails的新手,所以欢迎对我的代码发表进一步的评论!

Hpricot在首次出现时非常出色,因为它将CSS选择器语法引入了HTML解析。然而,它从来都不是完全符合XPath的,特别是在您正在使用的XPath谓词语法方面。

我建议野村。这个库快速且维护良好,并且完全符合XPath1.0。有了它,你应该能够拉动车辆:

doc.search('//vehicle[line_number[text()=1234]]')

另外,稍微简化一下:您确实不需要嵌套谓词。这也将识别正确的车辆:

doc.search('//vehicle[line_number/text()=1234]')

相关内容

  • 没有找到相关文章

最新更新