Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 为什么xPath搜索在REXML中有效,而在Hpricot中无效?_Ruby On Rails_Ruby_Xpath_Nokogiri_Hpricot - Fatal编程技术网

Ruby on rails 为什么xPath搜索在REXML中有效,而在Hpricot中无效?

Ruby on rails 为什么xPath搜索在REXML中有效,而在Hpricot中无效?,ruby-on-rails,ruby,xpath,nokogiri,hpricot,Ruby On Rails,Ruby,Xpath,Nokogiri,Hpricot,我使用的是Rails 3.2和Hpricot 我希望通过子元素的内容找到一个XML元素,并将其转换为Ruby对象,稍后将呈现该对象 换句话说,我想找到'vehicle'元素,其子元素'line\u number'内容等于1234 这在REXML和以下xPath中运行良好: /gsip/vehicle[line_number[text()=1234]] REXML速度很慢,因此我切换到Hpricot,在Hpricot中,相同的xPath查找所有车辆元素,而不仅仅是'line\u number'等

我使用的是Rails 3.2和Hpricot

我希望通过子元素的内容找到一个XML元素,并将其转换为Ruby对象,稍后将呈现该对象

换句话说,我想找到
'vehicle'
元素,其子元素
'line\u number'
内容等于
1234

这在REXML和以下xPath中运行良好:

/gsip/vehicle[line_number[text()=1234]]
REXML速度很慢,因此我切换到Hpricot,在Hpricot中,相同的xPath查找所有车辆元素,而不仅仅是
'line\u 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>

02.07.2012
1234
基础知识
VIN
2012
02.07.2012
8348
阿布德
VIN
2012
...
...

更新

我切换到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] =~ /^[\d\w]{6}$/
      num = 'pa_number'
    elsif params[:id] =~ /^[\d\w]{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
类意外事件控制器date.today-10.years}使用非常旧的日期启动
#logger.info(“***在XML.xPath:#{q}中查找车辆数据”)
doc=Nokogiri::XML(File.read(文件路径))
doc.xpath(q)|
vehicle_date=date.strtime(e.xpath('date').first.content,“%d.%m.%Y”)
#logger.info(“日期:{vehicle_Date.to_s}”)
如果车辆日期>车辆数据[:日期]
e、 所有的孩子?多恩|
@车辆数据[n.name]=n.content
结束
结束
结束
回应待办事项|格式|
format.html{重定向到连接器路径}
format.json{render:json=>@vehicle_data}
format.xml{render:xml=>@vehicle_data}
结束
结束
# ...
结束

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

Hpricot首次出现时非常棒,因为它将CSS选择器语法引入HTML解析。然而,它并不是完全兼容XPath的,尤其是在您正在使用的XPath谓词语法方面

我建议。该库速度快,维护良好,完全符合XPath 1.0。使用它,您应该能够拉动车辆:

doc.search('//vehicle[line_number[text()=1234]]')
另外,稍微简化一下:您实际上不需要嵌套谓词。这也将识别正确的车辆:

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

谢谢你,马克!Nokogiri太棒了!我的请求从4秒下降到250毫秒!我将我的控制器代码粘贴为上面的更新。我强烈建议您不要为每个用户请求阅读和解析5.6MB的文档。我不知道你希望同时有多少用户,但它的规模不会很高。考虑一下解析文档并存储它。然后只需要在一次行动中进行搜索。是的,你是对的。该应用程序仅供内部网使用,我预计一次最多有两名用户。我必须每天三次更新XML文件。我还考虑将文件推送到数据库表/模型中(不过您喜欢在Rails中称之为数据库表/模型)。我会带着这个文件去,可能很快就会修改它。