17

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?

1
  • Good question, +1. See my answer for a correct solution and for a detailed explanation of the problem. Commented Oct 15, 2010 at 1:45

2 Answers 2

29

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Answer revoked, +1, since I assume you actually know what you're talking about. I don't know Xpath, and just guessed xD
@Matchu: Yes, I do know XPath and happen to be ranked #1 by rep in this tag. :) But your answer was correct -- you needn't delete it. Undelete it and I'll upvote.
Thanks :) I'm usually pretty picky about just letting one answer be up when there are dupes within seconds, though, since my OCD wins out over my insatiable hunger for rep. Thanks, though! You are a gentleman and a scholar :o
@Matchu: Please... Undelete your answer. I want to upvote it.
6

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

1 Comment

Wow... I saw your undelete only now. Of course, the fully deserved +1. And I admired your explanation of the RegEx that decides if a number representation is composite!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.