Reputation: 16216
I have the following XML:
<links>
<item>
<title>Title 1</title>
<url>http://www.example.com/url-1</url>
</item>
<item>
<title>Title 2</title>
<url>http://www.example.com/url-2</url>
</item>
<item>
<title>Title 3</title>
<url>http://www.example.com/url-3</url>
</item>
</links>
And, I would like to convert it to a HTML list:
<ul>
<li><a href="http://www.example.com/url-1">Title 1</a></li>
<li><a href="http://www.example.com/url-2">Title 2</a></li>
<li><a href="http://www.example.com/url-3">Title 3</a></li>
</ul>
Currently I have this:
Controller:
require 'nokogiri'
doc = Nokogiri::XML(...)
@links = doc.xpath('//links/item').map do |i|
{'title' => i.xpath('//title'), 'url' => i.xpath('//url')}
end
Template:
<ul>
<% @links.each do |l| %>
<li><a href="<%= l['url'] %>"><%= l['title'] %></a></li>
<% end %>
</ul>
Resulting HTML:
<ul>
<li><a href="http://www.example.com/url-1http://www.example.com/url-2http://www.example.com/url-3">Title 1Title 2Title 3</a></li>
<li><a href="http://www.example.com/url-1http://www.example.com/url-2http://www.example.com/url-3">Title 1Title 2Title 3</a></li>
<li><a href="http://www.example.com/url-1http://www.example.com/url-2http://www.example.com/url-3">Title 1Title 2Title 3</a></li>
</ul>
What am I doing wrong? Is there a more optimal way of doing this?
Upvotes: 17
Views: 13025
Reputation: 243459
Replace this:
@links = doc.xpath('//links/item').map do |i|
{'title' => i.xpath('//title'), 'url' => i.xpath('//url')}
with:
@links = doc.xpath('//links/item').map do |i|
{'title' => i.xpath('title'), 'url' => i.xpath('url')}
Explanation:
//title
and
//url
are absolute XPath expressions and they select all (respectively) title
and all url
elements in the XML document.
Contrast this with:
title
and
url
These are relative XPath expressions and select all (respectively) title
and url
children of the current node only.
Upvotes: 29
Reputation: 85784
The trouble here is that the Xpath //title
searches for titles from the root of the document, and so returns all title
tags. Using the Xpath title
searches within the context of the given node, like you want. Ditto on url
.
@links = doc.xpath('//links/item').map do |i|
{'title' => i.xpath('title'), 'url' => i.xpath('url')}
end
Upvotes: 6