tags:

views:

3247

answers:

3

What is the XPath expression that I would use to get the string following 'HarryPotter:' for each book.

ie. Given this XML:

<bookstore>
<book>
  HarryPotter:Chamber of Secrets 
</book>
<book>
  HarryPotter:Prisoners in Azkabahn 
</book>
</bookstore>

I would get back:

Chamber of Secrets
Prisoners in Azkabahn

I have tried something like this:

/bookstore/book/text()[substring-after(. , 'HarryPotter:')]

I think my syntax is incorrect...

+4  A: 

In your XML you've got no space between Harry Potter but in your XQuery you've got a space. Just make it match and presto, you'll get data back...

    Dim xml = <bookstore>
                  <book>HarryPotter:Chamber of Secrets</book>
                  <book>HarryPotter:Prisoners in Azkabahn</book>
                  <book>MyDummyBook:Dummy Title</book>
              </bookstore>

    Dim xdoc As New Xml.XmlDocument
    xdoc.LoadXml(xml.ToString)

    Dim Nodes = xdoc.SelectNodes("/bookstore/book/text()[substring-after(., 'HarryPotter:')]")

    Dim Iter = Nodes.GetEnumerator()
    While Iter.MoveNext
        With DirectCast(Iter.Current, Xml.XmlNode).Value
            Console.WriteLine(.Substring(.IndexOf(":") + 1))
        End With
    End While
BenAlabaster
sorry - that string had a space by mistake... I've fixed it now.You do the string manipulation in VB (not in the XPath). I'm trying to use the XPath expression is get everything after the 'HarryPotter:' string in each selected node. Any ideas?
@Ben: there is no XQuery here... just an XPath. Also, the use of substring-after() inside a predicate is pretty clearly a mistake on the OP's part, not something we want to perpetuate.
LarsH
+2  A: 

The XPath expression

/bookstore/book/text()[substring-after(. , 'HarryPotter:')]

will return a node set with all text nodes containing the value "HarryPotter:". To get the book title succeeding "HarryPotter:" you need to go through this node set and fetch that substring for each node. This can be done with substring-after if you're using XSLT or with Substring and IndexOf if you're using VB as shown by balabaster.

Johan L
+5  A: 

In XPath 2.0 this can be produced by asingle XPath expression:

      /*/*/substring-after(., 'HarryPotter:')

Here we are using the very powerful feature of XPath 2.0 that at the end of a path of location steps we can put a function and this function will be applied on all nodes in the current result set.

In XPath 1.0 there is no such feature and this cannot be accomplished in one XPath expression.

We could perform an XSLT transformation like the following:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:template match="/">
      <xsl:for-each select="/*/*[substring-after(., 'HarryPotter:')]">
        <xsl:value-of select=
         "substring-after(., 'HarryPotter:')"/>
        <xsl:text>&#xA;</xsl:text>
      </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>  

When applied on the original XML document:

<bookstore>
    <book>  HarryPotter:Chamber of Secrets </book>
    <book>  HarryPotter:Prisoners in Azkabahn </book>
</bookstore>

this transformation produces the wanted result:

Chamber of Secrets
Prisoners in Azkabahn

Dimitre Novatchev
On the contrary, XPath 1.0 *does* have substring-after: http://www.w3.org/TR/xpath/#function-substring-after
Josh Stodola
@Josh-Stodola, Where did I say that it hasn't the substring-after() function? XPath 1.0 *doesn't allow* a function to be specified as a location step, regardles if this is substring-after(), or any other function. Please, revert your *incorrect* downvote!
Dimitre Novatchev