How to get parent node? - xpath

//* [ local-name()='component' and namespace-uri()='urn:hl7-org:v3' ]
Using thispath, I can get a node like this:
<?xml version="1.0" encoding="utf-8"?>
<ClinicalDocument xmlns="urn:hl7-org:v3">
<component>
<structuredBody>
<component>
<section>
<code code="10164-2" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
<title>History of Present Illness</title>
<text>
</text>
</section>
</component>
<component> ...... </component>
<component> ...... </component>
</structuredBody>
</component>
</ClinicalDocument>
In order to get the node as below:
<component>
<section>
<code code="10164-2" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
<title>History of Present Illness</title>
<text>
</text>
</section>
</component>
I change the path to :
//* [ local-name()='component' and namespace-uri()='urn:hl7-org:v3' and position()=1]
But, how can I get the same result by using [code="10164-2"] as a qualification?
edit 2012-12-17
//:component[1]//:component[.//:section/:code[#code='10164-2']]
this xpath works well and I can get the node I want . What should I do If I use
//*[local-name()='component' and namespace-uri()='urn:hl7-org:v3'])[1]
to select the parent <component/>node then add [#code='10164-2'] to the predicate part to get the <component/>child node i want. (I would like not to use : in the path to avoid namespace issue)

Use:
((//*[local-name()='component'
and namespace-uri()='urn:hl7-org:v3']
)[1]
//*[local-name()='component'
and
namespace-uri()='urn:hl7-org:v3'
]
)[1]
XSLT - based verification:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select=
"((//*[local-name()='component'
and namespace-uri()='urn:hl7-org:v3']
)[1]
//*[local-name()='component'
and
namespace-uri()='urn:hl7-org:v3']
)[1]
"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<component xmlns="urn:hl7-org:v3">
<structuredBody>
<component>
<section>
<code code="10164-2" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
<title>History of Present Illness</title>
<text>
</text>
</section>
</component>
<component> ...... </component>
<component> ...... </component>
</structuredBody>
</component>
the XPath expression is evaluated and the results of this evaluation are copied to the output:
<component xmlns="urn:hl7-org:v3">
<section>
<code code="10164-2" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC"/>
<title>History of Present Illness</title>
<text/>
</section>
</component>

Just add and section/code/#code='10164-2' to your predicate:
//*:component[namespace-uri()='urn:hl7-org:v3' and section/code/#code='10164-2']
Note: You may have to remove namespace-uri()='urn:hl7-org:v3' if component isn't in that namespace. Your example in "in order to get the node as below:" does not have a namespace.
Also, I used *:component instead of local-name() since you tagged the question XPath 2.0.

Related

XPath preceding depth-first tree element

I have a tree structure:
<pages>
<page id="1">
<page id="1A" />
<page id="1B" />
</page>
<page id="2" />
<page id="3">
<page id="3A" />
<page id="3B" />
<page id="3C" />
</page>
</pages>
I want to emit "previous" links with XSLT. I have succeeded in creating "next" links.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/pages">
<pages>
<xsl:for-each select=".//page">
<page id="{#id}" prev="???" next="{(child::page|following::page)/#id}" />
</xsl:for-each>
</pages>
</xsl:template>
</xsl:stylesheet>
Expected output:
$ xsltproc test.xsl test.xml
<?xml version="1.0"?>
<pages>
<page id="1" prev="" next="1A"/>
<page id="1A" prev="1" next="1B"/>
<page id="1B" prev="1A" next="2"/>
<page id="2" prev="1B" next="3"/>
<page id="3" prev="2" next="3A"/>
<page id="3A" prev="3" next="3B"/>
<page id="3B" prev="3A" next="3C"/>
<page id="3C" prev="3B" next=""/>
</pages>
I think
<page id="{#id}" prev="{(ancestor::page[1]/#id|preceding::page[1]/#id)[last()]}" next="{(child::page|following::page)/#id}" />
is what you are looking for.
Another way you could look at it:
XSLT 1.0 (with a node-set() extension function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/pages">
<xsl:variable name="all-pages">
<xsl:copy-of select=".//page"/>
</xsl:variable>
<pages>
<xsl:for-each select="exsl:node-set($all-pages)/page">
<page id="{#id}" prev="{preceding-sibling::page[1]/#id}" next="{following-sibling::page[1]/#id}" />
</xsl:for-each>
</pages>
</xsl:template>
</xsl:stylesheet>

XSLT Change attribute value of specific elements with a condition

I have the following XML content:
<fragment>
<directory Id="dirABC" Name="ABC">
<component Id="cmpA" Guid="*">
<file Id="filA" KeyPath="yes" Source="SourceRootDir\AAA.exe" />
</component>
<component Id="cmpB" Guid="*">
<file Id="filB" KeyPath="yes" Source="SourceRootDir\BBB.exe" />
</component>
<component Id="cmpC" Guid="*">
<file Id="filC" KeyPath="yes" Source="SourceRootDir\CCC.exe" />
</component>
</directory>
</fragment>
I am trying to find the file elements that have the Source attribute ending with 'BBB.exe' and replace its Id attribute with another value, e.g. with filNEW. In my example the Id attribute value 'filB' should be changed to 'filNEW'. So, my XSLT is defined the following way:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/fragment/directory/component/file[substring(#Source, string-length(#Source) - string-length('BBB.exe') + 1) = 'BBB.exe']">
<xsl:attribute name="Id">filNEW</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
However, instead of updating only the Id attribute of the file element of the second component element, the result has the whole file element removed for the second component and has the component element's Id attribute value replaced with the new filNEW value. The current result.
<?xml version="1.0" encoding="UTF-8"?>
<fragment>
<directory Id="dirABC" Name="ABC">
<component Guid="*" Id="cmpA">
<file Id="filA" KeyPath="yes" Source="SourceRootDir\AAA.exe"/>
</component>
<component Guid="*" Id="filNEW"/>
<component Guid="*" Id="cmpC">
<file Id="filC" KeyPath="yes" Source="SourceRootDir\CCC.exe"/>
</component>
</directory>
</fragment>
Whereas, I am trying to get this result:
<?xml version="1.0" encoding="UTF-8"?>
<fragment>
<directory Id="dirABC" Name="ABC">
<component Guid="*" Id="cmpA">
<file Id="filA" KeyPath="yes" Source="SourceRootDir\AAA.exe"/>
</component>
<component Id="cmpB" Guid="*">
<file Id="filNEW" KeyPath="yes" Source="SourceRootDir\BBB.exe" />
</component>
<component Guid="*" Id="cmpC">
<file Id="filC" KeyPath="yes" Source="SourceRootDir\CCC.exe"/>
</component>
</directory>
</fragment>
What am I doing wrong in the XSLT?
You are matching on the file element and then generating an attribute. If you just want to replace the value of the #Id attribute, then change your match expression to match on that #Id by adding /#Id after the predicate on the file:
<xsl:template match="/fragment/directory/component/file[substring(#Source, string-length(#Source) - string-length('BBB.exe') + 1) = 'BBB.exe']/#Id">
<xsl:attribute name="Id">filNEW</xsl:attribute>
</xsl:template>

XSLT 2.0 Sort by variable from another style sheet

I need to create XML that is sorted by numeric values I pull from another XSLT, which I use as a cross reference. The below source XML (source.xml) has four alpha characters at Partner/Header/#whse.
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<Header whse="NCCH" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="TXAU" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="LANO" >
<Contract claimNumber="00000000" />
</Header>
<Header whse="MIGR">
<Contract claimNumber="00000000" />
</Header>
<Header whse="TXHO">
<Contract claimNumber="00000000" />
</Header>
</Partner>
I need to cross reference the alpha characters to get the DUNS+4.
I use this XSLT (Duns_config.xslt) to get the DUNS.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="SHIPTODUNS">
<xsl:param name="Whse" />
<xsl:choose>
<xsl:when test="$Whse = 'LANO'"><xsl:value-of select="'0044893600101'" /></xsl:when>
<xsl:when test="$Whse = 'TXHO'"><xsl:value-of select="'0044893600103'" /></xsl:when>
<xsl:when test="$Whse = 'TXAU'"><xsl:value-of select="'0044893600105'" /></xsl:when>
<xsl:when test="$Whse = 'NCCH'"><xsl:value-of select="'0044893600214'" /></xsl:when>
<xsl:when test="$Whse = 'MIGR'"><xsl:value-of select="'8949713340601'" /></xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In the main XSLT (Transaction.xslt), I include the Duns_config.xslt and call SHIPTODUNS putting the data in the variable $headerDuns. I then get the last three digits of the DUNS+4 and put them into the variable $varWhse and try to sort by this variable:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<Partner partnerId="{./#partnerId}">
<xsl:apply-templates select="./Header" />
</Partner>
</xsl:template>
<xsl:template match="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="./#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<xsl:for-each select="current()">
<xsl:sort select="$varWhse" />
<transaction varwhse="{$varWhse}">
<duns number="{$headerDuns}" />
</transaction>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The output is not sorted by the $varWhse:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
</Partner>
I am wanting the data to come out like this:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
</Partner>
Anyone see what I am doing wrong or have another way? This is my first post on this site. It's a lot of information and I hope it makes sense.
One of the problems with your code is that the xsl:sort in your Header template is not the first instruction; xsl:sort has to be the first instruction. Another problem is that the sorting appears in the Header template, but it would have to be applied on an upper level. To correct these mistakes, some restructuring of your templates has to be performed.
For the following solution I chose to put all of the logic into one template - with two steps:
Fill a variable named mapping with a sub-data-structure with item elements.
Iterate over that variable with a sorted xsl:for-each and output the transaction elements.
The result looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<Partner partnerId="{./#partnerId}">
<xsl:variable name="mapping">
<xsl:for-each select="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<item>
<whse><xsl:value-of select="$varWhse" /></whse>
<duns><xsl:value-of select="$headerDuns" /></duns>
</item>
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$mapping/item">
<xsl:sort select="whse" /> <!-- See how xsl:sort is the first instruction -->
<transaction varwhse="{whse}">
<duns number="{duns}"/>
</transaction>
</xsl:for-each>
</Partner>
</xsl:template>
</xsl:stylesheet>
And its output is:
<?xml version="1.0" encoding="UTF-8"?>
<Partner partnerId="TradingPartner1">
<transaction varwhse="101">
<duns number="0044893600101"/>
</transaction>
<transaction varwhse="103">
<duns number="0044893600103"/>
</transaction>
<transaction varwhse="105">
<duns number="0044893600105"/>
</transaction>
<transaction varwhse="214">
<duns number="0044893600214"/>
</transaction>
<transaction varwhse="601">
<duns number="8949713340601"/>
</transaction>
</Partner>
Which is as desired.
Apply sorting after getting the result of choose. What happen is you are still generating the xml output, but you are sorting it on the Header item level.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:include href="Duns_config.xslt"/>
<xsl:template match="Partner">
<xsl:variable name="partial">
<PartnerTemp partnerId="{./#partnerId}">
<xsl:apply-templates select="./Header" />
</PartnerTemp>
</xsl:variable>
<xsl:apply-templates select="$partial"></xsl:apply-templates>
</xsl:template>
<xsl:template match="PartnerTemp">
<Partner partnerId="{./#partnerId}">
<xsl:perform-sort select="transaction">
<xsl:sort select="#varwhse"/>
</xsl:perform-sort>
</Partner>
</xsl:template>
<xsl:template match="Header">
<xsl:variable name="headerDuns">
<xsl:call-template name = "SHIPTODUNS">
<xsl:with-param name="Whse" select="./#whse" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="varWhse">
<xsl:value-of select="substring($headerDuns, 11, 3)" />
</xsl:variable>
<xsl:for-each select="current()">
<transaction varwhse="{$varWhse}">
<duns number="{$headerDuns}" />
</transaction>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Sorting a table in XSLT 2.0

I have an index file, containing a list of xml files to process:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<part>
<numberGroup format="1" start="1">
<entry>
<file>04.xml</file>
<title>first file</title>
<css>stylesheet.css</css>
</entry>
<entry>
<file>05.xml</file>
<title>second file</title>
<css>stylesheet.css</css>
</entry>
<entry>
<file>06.xml</file>
<title>third file</title>
<css>stylesheet.css</css>
</entry>
.... more files
</numberGroup>
.... more NumberGroups
</part>
....more parts
</list>
every file 01.xml etc has one HTML-style table, like this:
<table class='wl'>
<tr class='header'>
<td><p>English</p></td>
<td><p>French</p></td>
</tr>
<tr>
<td><p>belly</p></td>
<td><p>ventre</p></td>
</tr>
<tr>
<td><p>leg</p></td>
<td><p>jambe</p>/td>
</tr>
... etc
</table>
I want to merge all the tables (3 in this example) into one, so I get a compete vocabulary.
So far I have this stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:result-document href="wl.xml">
<xsl:text disable-output-escaping="yes">
<!DOCTYPE html>
</xsl:text>
<html xmlns:epub="http://www.idpf.org/2007/ops" lang="nl" xml:lang="nl">
<head>
<title>
<xsl:value-of select="./title"/>
</title>
<xsl:apply-templates select="css"/>
</head>
<body>
<table>
<xsl:apply-templates select="//numberGroup[1]/entry">
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:result-document>
</xsl:template>
<xsl:template match="entry">
<xsl:apply-templates select="document(file)//table[#class='wl']/tr[not(contains(#class, 'header'))]"/>
</xsl:template>
<xsl:template match="tr">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
which merges as desired, except for the sorting.
How can I sort the generated table alphabetically, on the first column?
Replace
<xsl:apply-templates select="//numberGroup[1]/entry">
</xsl:apply-templates>
with
<xsl:apply-templates select="document(//numberGroup[1]/entry/file)//table[#class='wl']/tr[not(contains(#class, 'header'))]">
<xsl:sort select="td[1]" data-type="text"/>
</xsl:apply-templates>
then you can drop the template for entry elements.

XPath query for empty namespace xmlns=""

What XPath do I use to query the info node in the xml below? I've tried different expressions in XMLSpy but nothing works.
<root xmlns="tempuri.org" xmlns:p="http://nonamespace.org/std/Name/2006-10-18/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item xmlns="">
<info>blah blah</info>
<date>2009-07-27 00:00:00</date>
</item>
you can do it like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="tempuri.org">
<xsl:template match="/">
<xsl:value-of select="a:root/item/info"/>
</xsl:template>

Resources