5

So I have an XML document of employees in my system:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<couriersystem title="System"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="schema.xsd">
    <!-- snip -->
    <employees>
        <employee eid="1">
            <nin>AZ123518D</nin>
            <firstname>Peter</firstname>
            <lastname>Smith</lastname>
            <gender>Male</gender>
            <dob>1994-02-11</dob>
            <email>ps11@gmail.com</email>
            <address>
                119, London Street, Nidrie, F57 8NE
            </address>
            <tel>07005748900</tel>
            <salary>30526</salary>
            <empbranch bid="1" />
            <supervisor sid="1" />
        </employee>
        <employee eid="2">
            <nin>CN174869F</nin>
            <firstname>Jennifer</firstname>
            <lastname>Black</lastname>
            <gender>Male</gender>
            <dob>1984-12-24</dob>
            <email>jb21@gmail.com</email>
            <address>
                161, South Road, Nidrie, W79 8WG
            </address>
            <tel>07555111222</tel>
            <salary>40576</salary>
            <empbranch bid="2" />
            <supervisor sid="1" />
        </employee>
        <employee eid="3">
            <nin>ET127654M</nin>
            <firstname>Aaron</firstname>
            <lastname>Jones</lastname>
            <gender>Male</gender>
            <dob>1968-03-15</dob>
            <email>aj31@gmail.com</email>
            <address>
                66, High Road, Yoker, Q47 4SR
            </address>
            <tel>07856471267</tel>
            <salary>30526</salary>
            <empbranch bid="3" />
            <supervisor sid="1" />
        </employee>
        <employee eid="4">
            <nin>GC765238A</nin>
            <firstname>Alistair</firstname>
            <lastname>Smith</lastname>
            <gender>Male</gender>
            <dob>1976-11-26</dob>
            <email>as11@gmail.com</email>
            <address>
                109, West Plaza, Clydebank, G55 8RC
            </address>
            <tel>07000123123</tel>
            <salary>25400</salary>
            <empbranch bid="4" />
            <supervisor sid="1" />
        </employee>
        <employee eid="5">
            <nin>HP146854D</nin>
            <firstname>Emma</firstname>
            <lastname>Reynolds</lastname>
            <gender>Male</gender>
            <dob>1995-05-05</dob>
            <email>er11@yahoo.com</email>
            <address>
                57, Scott Street, Aberdeen, O75 2KS
            </address>
            <tel>07625361536</tel>
            <salary>25400</salary>
            <empbranch bid="5" />
            <supervisor sid="7" />
        </employee>
        <!-- snip -->
    </employees>
    <!-- snip -->
</couriersystem>

And I have the following XPath to retrieve all of the employee details by their supervisor:

//employee[supervisor/@sid='1']/concat('Name: ', concat(firstname/text(), ' ', lastname/text(), '\nGender: ', gender/text(), '\nD.O.B: ', dob/text()))

But it only shows one employee out of the rest of them, and the \n characters show as well.

How can I fix this issue?

kjhughes
  • 106,133
  • 27
  • 181
  • 240
madcrazydrumma
  • 1,847
  • 3
  • 20
  • 38

3 Answers3

11

To get \n to be interpreted in concat() in XPath, use codepoints-to-string(10) [credit: @madcrazydrumma]:

//employee[supervisor/@sid='1']/concat('Name: ', firstname, ' ', lastname, 
                                       codepoints-to-string(10),
                                       'Gender: ', gender, 
                                       codepoints-to-string(10),
                                       'D.O.B: ', dob)

which will then return results as follows:

Name: Peter Smith
Gender: Male
D.O.B: 1994-02-11
Name: Jennifer Black
Gender: Male
D.O.B: 1984-12-24
Name: Aaron Jones
Gender: Male
D.O.B: 1968-03-15
Name: Alistair Smith
Gender: Male
D.O.B: 1976-11-26

Minor note: Your basic XPath 2.0 expression turned out to be fine. As we discovered in the comments, the results were incorrect due to limitations of the site, xpathtester.com/xpath. Use videlibri.sourceforge.net/cgi-bin/xidelcgi instead for online XPath 2.0 testing.

Community
  • 1
  • 1
kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • Its only returning one result though, which is the strangest thing! – madcrazydrumma Mar 30 '16 at 15:57
  • What XPath 2.0 library are you using? – kjhughes Mar 30 '16 at 15:58
  • 1
    I've seen such problems with that site's XPath 2.0 implementation. Try this site instead: http://videlibri.sourceforge.net/cgi-bin/xidelcgi – kjhughes Mar 30 '16 at 16:06
  • @kjhuges that solves the issue! But how can i use new lines? – madcrazydrumma Mar 30 '16 at 16:10
  • I used `codepoints-to-string(10)` to add a new line in XPath 2.0. You can update your code if you like to show others, thank you for the new site! – madcrazydrumma Mar 30 '16 at 16:25
  • How do you check your version of XPath? – Yzmir Ramirez Dec 20 '20 at 06:34
  • @YzmirRamirez: There is no XPath standard way to determine the version of an XPath implementation. See the general probe technique given in my answer to [Does Chrome use XPath 2.0](https://stackoverflow.com/q/25455351/290085). (Web browsers only support 1.0 currently (with no signs of advancing)). – kjhughes Dec 20 '20 at 15:14
  • 1
    I was flailing around trying to figure out how to detect an XML element containing line breaks and this was the first mention of codepoints-to-string() I've seen; thank you! All my attempts to specify '\n' or '\r' weren't working. But this worked perfectly for me in Altova XMLSpy: `/PNRList/PNR/SegmentList/HotelSegment/Address[contains(text(), codepoints-to-string(10))]` – ALEXintlsos Mar 08 '22 at 17:09
  • 1
    This is nice. If you want to insert multiple newlines, it turns out there's a form `codepoints-to-string((10, 10))` (note the nested parentheses to form a sequence). – Garret Wilson Jun 19 '23 at 13:59
2

Well, I tested your XPath expression with XSLT and the output contains 4 entries as you desired:

My XSLT was:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/couriersystem">
    <xsl:copy-of select="//employee[supervisor/@sid='1']/concat('Name: ', concat(firstname/text(), ' ', lastname/text(), '\nGender: ', gender/text(), '\nD.O.B: ', dob/text()))" />
  </xsl:template>
</xsl:stylesheet>

The result is all in one line of text:

<?xml version="1.0" encoding="UTF-8"?>Name: Peter Smith\nGender: Male\nD.O.B: 1994-02-11 Name: Jennifer Black\nGender: Male\nD.O.B: 1984-12-24 Name: Aaron Jones\nGender: Male\nD.O.B: 1968-03-15 Name: Alistair Smith\nGender: Male\nD.O.B: 1976-11-26

If you like to have the output a little bit more formatted than the above, try this expressions which inserts space characters at the appropriate places:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/couriersystem">
    <xsl:value-of select="'&#10;'" />
    <xsl:for-each select="//employee[supervisor/@sid='1']">
      <xsl:value-of select="concat('Name: ', firstname/text(), ' ', lastname/text(), '&#10;','Gender: ', gender/text(), '&#10;','D.O.B: ', dob/text(),'&#10;')" />
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

The result of this is:

<?xml version="1.0" encoding="UTF-8"?>
Name: Peter Smith
Gender: Male
D.O.B: 1994-02-11
Name: Jennifer Black
Gender: Male
D.O.B: 1984-12-24
Name: Aaron Jones
Gender: Male
D.O.B: 1968-03-15
Name: Alistair Smith
Gender: Male
D.O.B: 1976-11-26
zx485
  • 28,498
  • 28
  • 50
  • 59
  • Haha my friend input the data so maybe! ;) and I'm not using XSL for this, so that unfortunately doesn't solve the issue – madcrazydrumma Mar 30 '16 at 16:08
  • 1
    Well, my last remark was just a little joke *g*. However, my XPath expression is basically identical to the accepted answer, just replace ` ` with `codepoints-to-string(10)` and merge the expressions and your done. – zx485 Mar 30 '16 at 17:00
1

codepoints-to-string(10) will work as part of the XPath expression, but a more direct approach is simply to insert a literal newline (U+00A0 is the actual Unicode code point) into the XPath expression. The trick is that how you do that depends on the "host language", that is, where you are writing the XPath expression.

If your XPath expression is inside XML, if you use \n, you're not passing a newline but instead the characters \ and n, which XPath does not interpret as an escape sequence. You would need to use &#xa;, which XML considers to be a reference to a newline; an actual newline will then be inserted into the actual XPath expression by the XML processor, and the XPath processor will only see the literal newline character.

Similarly in Java you would use the \n escape sequence in a Java string, because Java recognizes this as an escape sequence and translates this to an actual newline character in the string. In this case as well XPath processor would only see the literal newline character.

See my other answer for more explanation and discussion.

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272