Ruby on rails 为什么xPath搜索在REXML中有效,而在Hpricot中无效?
我使用的是Rails 3.2和Hpricot 我希望通过子元素的内容找到一个XML元素,并将其转换为Ruby对象,稍后将呈现该对象 换句话说,我想找到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'等
'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中称之为数据库表/模型)。我会带着这个文件去,可能很快就会修改它。