Home > Software design >  How to select a tag which contains another tag with a specific value in Nokogiri?
How to select a tag which contains another tag with a specific value in Nokogiri?

Time:12-02

I have an xml that looks like this

<list>
<item>
  <id>1</id>
  <data>123</data>
</item>
<item>
  <id>2</id>
  <data>abc</data>
</item>
</list>

Is there any builtin function in nokogiri that allows me to get an <item> that have an specific <id>?

Something that would look like this:

require 'nokogiri'

xml = '
<list>
<item>
  <id>1</id>
  <data>123</data>
</item>
<item>
  <id>2</id>
  <data>abc</data>
</item>
</list>
'

data = Nokogiri::XML(xml)

item = data.xpath("//item:contains('id', '2')")

I could achieve it by item = data.xpath("//item").select {|d| d.xpath("id").text == "1"} but is there any builtin way to achieve this? `

CodePudding user response:

Following the comments I'll put it in the answer.

item = data.xpath("//id[text() = '2']/parent::node()")

This is a solution with xpath. Instead of iterating through items one can directly access what is needed. If I do a benchmark on this solution it shows 0.000068 compared to your approach item = data.xpath("//item").select {|d| d.xpath("id").text == "1"} which scores 0.000121, basically a 2 times improvement.


As for Slop, it's a decorator that will make your code look more "builtin", native, whatever. First you'll need to change the way how you acquire data: data = Nokogiri::Slop(xml), Slop goes instead of XML. The code to achieve what you need will be:

item = data.list.item.select{|x| x.id.text == '2'}

As you see it lacks those xpath strings, utilizes ruby methods:

The Slop decorator implements method_missing such that methods may be used instead of CSS or XPath. source: w3cub

So, yeah, it looks cool

  • Related