XSLT: How to peek nested element - xslt

That's my xml I need to apply an xslt:
<document>
<component>
<structuredBody>
<component>
<section>
<identifier code="S001"/>
<...>
</section>
</component>
</structuredBody>
</component>
</document>
As you can see, here there's a lot of nested structure I don't need.
What I'm only need is to peek section element where section>identifier.code = "S001".
I'd like to peek my desired element without taking care of upper structure.
I'm using this xslt but it's not peeking my desired section element:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="//section[identifier/#code = 'S001']"/>
</xsl:template>
</xsl:stylesheet>
But I'm getting:
<?xml version="1.0" encoding="UTF-8"?>
Above example was a reduced effort to simplify my problem:
<document>
<component>
<structuredBody>
<component>
<section>
<identifier code="S001"/>
<table>
<tbody>
<tr>
<td>attribute1</td>
<td>value1</td>
</tr>
<tr>
<td>attribute2</td>
<td>value2</td>
</tr>
<tr>
<td>attribute3</td>
<td>value3</td>
</tr>
</tbody>
</table>
</section>
<section>
<identifier code="S002"/>
<table>
...
</table>
</section>
</component>
</structuredBody>
</component>
</document>
What I really need is to get something like:
<person> <!-- -> section-->
<attribute key="attribute1">value1</attribute>
<attribute key="attribute2">value2</attribute>
<attribute key="attribute3">value3</attribute>
</person>
Any ideas?

Try:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="//section[identifier/#code='S001']">
<person>
<xsl:for-each select="//td[1]">
<attribute key="{.}"><xsl:value-of select="../td[2]"/></attribute>
</xsl:for-each>
</person>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Remark: this is the solution to the edited post.

Related

extract the value of an element node via XPath?

Source-XML:
<data>
<item>
<values>
<element1>
<language>EN</language>
<text>text</text>
</element1>
<element2>
<language>DE</language>
<text>Text</text>
</element2>
</values>
</item>
<item>
<values>
<element5>
<language>EN</language>
<text>description</text>
</element5>
<element6>
<language>DE</language>
<text>Beschreibung</text>
</element6>
</values>
</item> </data>
I want to get all the elements in language 'EN'. First I have a loop, where I saved the elment names in a variable. In the next step I want to get only the elements in language "EN". I need in the result of this step only the element-name and text which have the language 'EN' to build a table.
I tried this:
<xsl:param name="element" select="'element1'"/>
<xsl:template match="/">
<xsl:if test="data/item/values[local-name()=$element]/language[text()='EN']">
</xsl:if>
</xsl:template>
And the output XSLT should be something like:
<table id="123">
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
<colspec colname="c2" colnum="2" colwidth="1.0*"/>
<thead>
<row>
<entry>Name</entry>
<entry>Values</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<ph>element1</ph>
</entry>
<entry>text</entry>
</row>
<row>
<entry>
<ph>element5</ph>
</entry>
<entry>description</entry>
</row>
</tbody>
</tgroup>
</table>
I know that there are other ways to solve this problem. But for other steps in the transformation it is important to test every element separately.
Thanks in advance!
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data">
<xsl:for-each-group select="item" group-by="values/*[language = 'EN']">
<xsl:for-each select="current-group()">
<xsl:element name="{current-group()/values/*[language = 'EN']/local-name()}">
<language>
<xsl:value-of select="descendant::language[text() = 'EN']"/>
</language>
<txt>
<xsl:value-of select="current-group()/values/*[language = 'EN']/text"/>
</txt>
</xsl:element>
</xsl:for-each>
</xsl:for-each-group>
</xsl:template>
You may do like this
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common" version="1.0">
<xsl:output indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:variable name="ENelements">
<xsl:for-each select="/data/item/values/*[language='EN']">
<element name="{local-name()}"><xsl:value-of select="text" /></element>
</xsl:for-each>
</xsl:variable>
<table id="123">
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
<colspec colname="c2" colnum="2" colwidth="1.0*"/>
<thead>
<row>
<entry>Name</entry>
<entry>Values</entry>
</row>
</thead>
<tbody>
<xsl:for-each select="exsl:node-set($ENelements)/*">
<row>
<entry>
<ph><xsl:value-of select="#name" /></ph>
</entry>
<entry><xsl:value-of select="." /></entry>
</row>
</xsl:for-each>
</tbody>
</tgroup>
</table>
</xsl:template>
</xsl:stylesheet>
http://xsltfiddle.liberty-development.net/jyRYYig
AFAICT, it could be simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/data">
<table id="123">
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
<colspec colname="c2" colnum="2" colwidth="1.0*"/>
<thead>
<row>
<entry>Name</entry>
<entry>Values</entry>
</row>
</thead>
<tbody>
<xsl:for-each select="item">
<xsl:variable name="elem" select="values/*[language='EN']" />
<row>
<entry>
<ph>
<xsl:value-of select="name($elem)"/>
</ph>
</entry>
<entry>
<xsl:value-of select="$elem/text"/>
</entry>
</row>
</xsl:for-each>
</tbody>
</tgroup>
</table>
</xsl:template>
</xsl:stylesheet>

Importing OAI source into Filemaker

I have a problem with importing an OAI source into Filemaker. The mapping is ok but the result is empty.
This is the source:
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dc="http://dublincore.org/documents/dcmi-namespace/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
<responseDate>2015-01-15T12:05:11Z</responseDate>
<request verb="ListRecords" metadataPrefix="oai_dc">
http://api.memorix-maior.nl/collectiebeheer/oai-pmh/key/SORRY_THIS_KEY_I_CANNOT_SHOW/tenant/nfm
</request>
<ListRecords>
<record>
<header>
<identifier>
e:1d59bf74-a57c-11e1-af90-bf6f69fae6b6:000a80bf-e7d6-7670-b2bd-c269b2e58878
</identifier>
etc.
And this is the xslt I made:
<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<METADATA>
<FIELD NAME="identifier" TYPE="TEXT"/>
</METADATA>
<RESULTSET>
<xsl:for-each select="OAI-PMH/ListRecords/record">
<ROW>
<COL>
<DATA><xsl:value-of select="header/identifier"/></DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
To make it clear I only pointed the first field in the OAI source.
I hope you can help me to fix this.
Best regards,
Boudewijn Ridder
The reason why your attempt doesn't work is that the source XML nodes are in a namespace. You must declare this namespace in your stylesheet, assign it a prefix and use that prefix when addressing the nodes:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:oai="http://www.openarchives.org/OAI/2.0/">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<METADATA>
<FIELD NAME="identifier" TYPE="TEXT"/>
</METADATA>
<RESULTSET>
<xsl:for-each select="oai:OAI-PMH/oai:ListRecords/oai:record">
<ROW>
<COL>
<DATA><xsl:value-of select="oai:header/oai:identifier"/></DATA>
</COL>
</ROW>
</xsl:for-each>
</RESULTSET>
</FMPXMLRESULT>
</xsl:template>
</xsl:stylesheet>
Note:
If your input example is representative, you might want to use :
<xsl:value-of select="normalize-space(oai:header/oai:identifier)"/>
to trim the extraneous whitespace from the result.

Using XPATHs from file and adding relevant attribute

May be my XSL approach is wrong? please correct me the way to handle this situation
I want to grab XPATHs and Attrs from a mapping file, then use XPATH to match, and apply attributes to XML.
Here is my 3 inputs files:
mappings.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<map xpath="//title" class="title" others="moreToCome" />
<map xpath="//subtitle" class="subtitle" others="moreToCome" />
<map xpath="//p" class="p" others="moreToCome" />
</mappings>
Source.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title>title text</title>
<subtitle>subtitle text</subtitle>
<p>subtitle text</p>
</root>
StyleMapping.xsl
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="fMappings" select="document('mappings.xml')" />
<xsl:variable name="xpath"><xsl:text>justToMakeItGlobal</xsl:text></xsl:variable>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
<!-- loop thru map in mappings.xml -->
<xsl:for-each select="$fMappings//mappings/map">
<xsl:call-template name="dyn">
<xsl:with-param name="xpath" select="#xpath" />
<xsl:with-param name="class" select="#class" />
<xsl:with-param name="others" select="#others" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template match="$xpath" mode="dyn">
<xsl:param name="xpath"/>
<xsl:param name="class"/>
<xsl:param name="others"/>
<xsl:attribute name="class"><xsl:value-of select="$class" /></xsl:attribute>
<xsl:attribute name="others"><xsl:value-of select="$others" /></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
This is what is planning to do, but i'm not getting the correct way of doing it in XSLT:
1. Read mappings.xml file
2. Loop thru each map tag
3. grab xpath and attr's
4. apply template match/select with above xpath
5. add attr's to above selected nodes
output.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title class="title" others="moreToCome">title text</title>
<subtitle class="subtitle" others="moreToCome">subtitle text</subtitle>
<p class="p" others="moreToCome">subtitle text</p>
</root>
I don't think you can have a template with a calculated match pattern. Let me suggest a different approach:
mappings.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings>
<map elem="title" class="Title" others="moreToCome1" />
<map elem="subtitle" class="Subtitle" others="moreToCome2" />
<map elem="p" class="P" others="moreToCome3" />
</mappings>
stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:variable name="mappings" select="document('mappings.xml')/mappings" />
<xsl:template match="*">
<xsl:variable name="elem" select="name()" />
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:if test="$elem = $mappings/map/#elem">
<xsl:attribute name="class">
<xsl:value-of select="$mappings/map[#elem=$elem]/#class"/>
</xsl:attribute>
<xsl:attribute name="others">
<xsl:value-of select="$mappings/map[#elem=$elem]/#others"/>
</xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Testing with the following source XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<title original="yes">title text</title>
<subtitle>subtitle text</subtitle>
<p>para text</p>
<nomatch attr="test">another text</nomatch>
</root>
results in:
<?xml version="1.0" encoding="utf-8"?>
<root>
<title original="yes" class="Title" others="moreToCome1">title text</title>
<subtitle class="Subtitle" others="moreToCome2">subtitle text</subtitle>
<p class="P" others="moreToCome3">para text</p>
<nomatch attr="test">another text</nomatch>
</root>

separate XML results into 2+ groups using XSL

I'm having trouble creating the XSLT to do something that I think should be straightforward...
Given the following XML:
<DATA_DS>
<LIST_ITEMS>
<ITEMS>
<abc>2011</abc>
<def>ABC Company</def>
<value>23</value>
</ITEMS>
<ITEMS>
<abc>2011</abc>
<def>ABC Company 1</def>
<value>11</value>
</ITEMS>
<ITEMS>
<abc>2010</abc>
<def>ABC Company 2</def>
<value>15</value>
</ITEMS>
<ITEMS>
<abc>2010</abc>
<def>ABC Company 3</def>
<value>6</value>
</ITEMS>
<ITEMS>
<abc>2010</abc>
<def>ABC Company 4</def>
<value>44</value>
</ITEMS>
</LIST_ITEMS></DATA_DS>
how would I go about transforming it using XSLT into something like this:
<table>
<row>23</row>
<row>11</row>
</table>
<table>
<row>15</row>
<row>6</row>
<row>44</row>
</table>
the idea is to create a new "table" every time a new "abc" is encountered
In xslt 2.0 instruction for-each-group is what you are looking for.
Following stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<output>
<xsl:for-each-group select="DATA_DS/LIST_ITEMS/ITEMS" group-by="abc">
<table>
<xsl:for-each select="current-group()">
<row>
<xsl:value-of select="current()/value" />
</row>
</xsl:for-each>
</table>
</xsl:for-each-group>
</output>
</xsl:template>
</xsl:stylesheet>
produces output
<?xml version="1.0" encoding="UTF-8"?>
<output>
<table>
<row>23</row>
<row>11</row>
</table>
<table>
<row>15</row>
<row>6</row>
<row>44</row>
</table>
</output>

How to use XML file stored at different location inside XSL file

I have below XML file stored at D:\test
<?xml version="1.0" encoding="utf-8"?>
<!--Your comments.-->
<root>
<Sample name="pal">
<Name>pal</Name>
<Age>24</Age>
<Job>software</Job>
</Sample>
</root>
I am trying to use it in below XSL file at D:\test\test1\test2:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:for-each select="root/sample">
<tr>
<td><xsl:value-of select="Name" /></td>
<td><xsl:value-of select="Age" /></td>
</tr>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I am not able to get the required result.