Reputation: 125
I am trying to make a transformation with XSLT. This is my XML source:
<Memoria>
<seccion>
<contenido><p>TEXT</p>
<ul>
<li>LIST</li>
</ul>
<p>ANOTHER TEXT</p>
<p>&nbsp;</p></contenido>
</seccion>
</Memoria>
I am trying use XSLT like this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Memoria">
<cosa>
<xsl:for-each select="seccion">
<Address>
<xsl:for-each select="contenido">
<FirstName><xsl:value-of select="p" /></FirstName>
</xsl:for-each>
<ul>
<LastName><xsl:value-of select="ul/li" /></LastName>
</ul>
</Address>
</xsl:for-each>
</cosa>
</xsl:template>
</xsl:stylesheet>
And get this:
<?xml version="1.0"?>
<cosa>
<Address>
<FirstName>TEXT</FirstName>
<ul>
<LastName>LIST</LastName>
</ul>
</Address>
</cosa>
But I expect:
<?xml version="1.0"?>
<cosa>
<Address>
<FirstName>TEXT</FirstName>
<ul>
<LastName>LISTr</LastName>
</ul>
<FirstName>ANOTHER TEXT</FirstName>
</Address>
</cosa>
I get this XML from a external application.
Upvotes: 1
Views: 147
Reputation: 12154
rare transformation? provided with a proper Input XML and desired output, it wouldn't have been rare in XSLT ..
Honestly I am not happy with your sample Input and output XML. I have come up with one XML hope this is what really your XML looks like. If not then bring up precise i/p and o/p XMLs.
<?xml version="1.0" encoding="utf-8"?>
<Memoria>
<seccion>
<contenido>
<p>TEXT</p>
<ul>
<li>Item1</li>
<li>Item2</li>
<li>Item3</li>
</ul>
<p>ANOTHER TEXT</p>
<ul>
<li>Itema</li>
<li>Itemb</li>
<li>Itemc</li>
</ul>
</contenido>
</seccion>
</Memoria>
Corresponding XSLT code:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<cosa>
<xsl:apply-templates select="//contenido"/>
</cosa>
</xsl:template>
<xsl:template match="text()"/>
<xsl:template match="contenido">
<Address>
<xsl:apply-templates select="p|ul"/>
</Address>
</xsl:template>
<xsl:template match="p[not(contains(.,'&'))]">
<FirstName>
<xsl:value-of select="."/>
</FirstName>
</xsl:template>
<xsl:template match="ul">
<ul>
<xsl:for-each select="li">
<LastName>
<xsl:value-of select="."/>
</LastName>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
Upvotes: 3
Reputation: 7662
I'm not sure what exactly you want to achieve... However, the following XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Memoria">
<cosa>
<xsl:for-each select="seccion">
<Address>
<xsl:apply-templates select="contenido" />
</Address>
</xsl:for-each>
</cosa>
</xsl:template>
<xsl:template match="contenido">
<xsl:apply-templates select="p | ul" />
</xsl:template>
<xsl:template match="p">
<FirstName>
<xsl:value-of select="text()"/>
</FirstName>
</xsl:template>
<xsl:template match="ul">
<ul>
<LastName>
<xsl:value-of select="li"/>
</LastName>
</ul>
</xsl:template>
</xsl:stylesheet>
will produce the following output:
<?xml version="1.0" encoding="UTF-8"?>
<cosa>
<Address>
<FirstName>TEXT</FirstName>
<ul>
<LastName>LIST</LastName>
</ul>
<FirstName>ANOTHER TEXT</FirstName>
<FirstName>&nbsp;</FirstName>
</Address>
</cosa>
You may now think of adding of condition to exclude that &
element from the output:
<xsl:template match="p">
<xsl:if test="not(contains(text(), '&'))">
<FirstName>
<xsl:value-of select="text()"/>
</FirstName>
</xsl:if>
</xsl:template>
Upvotes: 1
Reputation: 5258
Assuming you know for sure that you'll always have FirstName in first P element, and second first name in the 2nd P element, you can use a common node-set() function to get the specific element inside your for-each loop:
The following modified XSLT generates the desired output you have placed in your question:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/Memoria">
<cosa>
<xsl:for-each select="seccion">
<Address>
<xsl:for-each select="contenido">
<FirstName>
<xsl:value-of select="p" />
</FirstName>
<ul>
<LastName>
<xsl:value-of select="ul/li" />
</LastName>
</ul>
<FirstName>
<xsl:value-of select="exsl:node-set(.)/p[2]/."/>
</FirstName>
</xsl:for-each>
</Address>
</xsl:for-each>
</cosa>
</xsl:template>
</xsl:stylesheet>
Note: I usually use xmlns:msxsl="urn:schemas-microsoft-com:xslt" for the node-set() function but I think exsl is not as vendor specific :)
This is likely to break if your XML structure is not consistent or predictable; otherwise it will work and you don't need another for-each to get the 2nd P element for the second FirstName result element.
I hope this is helpful!
Upvotes: 0
Reputation: 7153
xsl:value-of
is going to get the contents of the first element in a node set. If you want to operate over all the elements in a set, you need xsl:for-each
or xsl:apply-templates
. However, if you do that, you will get:
<?xml version="1.0"?>
<cosa>
<Address>
<FirstName>TEXT</FirstName>
<ul>
<LastName>LISTr</LastName>
</ul>
<FirstName>ANOTHER TEXT</FirstName>
<FirstName>& </FirstName>
</Address>
</cosa>
Upvotes: 0