How to flatten this XML with XSLT
ENTRANCE
<logs>
<logentry revision="648">
<author>nshmyrev</author>
<date>2008-09-21T19:43:10.819236Z</date>
<paths>
<path action="M">/trunk/po/ru.pi</path>
</paths>
<msg>2008-09-21 Nickolay V. Shmyrev nshmyrev@yandex.ru * ru.po: Updated Russian translation.</msg>
</logentry>
<logentry revision="647">
<author>ckirbach</author>
<date>2008-09-21T16:25:58.369324Z</date>
<paths>
<path action="M">/trunk/po/de.po</path>
<path action="M">/trunk/po/ChangeLog</path>
</paths>
<msg>* de.po: Updated German translation.</msg>
</logentry>
<logs>
to
<logs>
<LogEntry revision="647" author = "ckirbach" action="M">/trunk/po/de.po</LogEntry>
<LogEntry revision="647" author = "ckirbach" action="M">/trunk/po/ChangeLog</LogEntry>
</logs
Also, I want to ignore all paths with an extension, say '.pi'
+1
Rajiv Das
source
to share
2 answers
Alastair's solution gives the wrong result: the value of the "author" attribute is "nshmyrev", but it should be: "ckirbach".
The solution below has been tested to work correctly.
Here is a solution that works for different extensions to be ignored (specified globally <xsl:param/>
).
This transformation:
<xsl: stylesheet version = "1.0" xmlns: xsl = "http://www.w3.org/1999/XSL/Transform"> <xsl: output omit-xml-declaration = "yes" indent = "yes" /> <xsl: param name = "pIgnExt" select = "'. pi'" /> <xsl: variable name = "vExtLen" select = "string-length ($ pIgnExt)" /> <xsl: template match = "logs"> <logs> <xsl: apply-templates select = "* / * / path" /> </logs> </ xsl: template> <xsl: template match = "path"> <xsl: variable name = "vthisLen" select = "string-length (.)" /> <xsl: if test = "not (substring (., $ vthisLen - $ vExtLen +1) = $ pIgnExt ) "> <LogEntry revision = "{../../@ revision}" author = "{../../ author}" action = "{@ action}"> <xsl: copy-of select = "node ()" /> </LogEntry> </ xsl: if> </ xsl: template> </ xsl: stylesheet>
when applied to the original XML document (fixed to be well-formed!):
<logs> <logentry revision = "648"> <author> nshmyrev </author> <date> 2008-09-21T19: 43: 10.819236Z </date> <paths> <path action = "M"> / trunk / po / ru.pi </path> </paths> <msg> 2008-09-21 Nickolay V. Shmyrev nshmyrev@yandex.ru * ru.po: Updated Russian translation. </msg> </logentry> <logentry revision = "647"> <author> ckirbach </author> <date> 2008-09-21T16: 25: 58.369324Z </date> <paths> <path action = "M"> / trunk / po / de.po </path> <path action = "M"> / trunk / po / ChangeLog </path> </paths> <msg> * de.po: Updated German translation. </msg> </logentry> </logs>
produces the desired output:
<logs> <LogEntry revision = "647" author = "ckirbach" action = "M"> / trunk / po / de.po </LogEntry> <LogEntry revision = "647" author = "ckirbach" action = "M"> / trunk / po / ChangeLog </LogEntry> </logs>
Note that this can be used as an XPath 1.0 implementation of the end-with () function , which is only available in XPath 2.0.
+1
source to share
What about
<?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" indent="yes"/>
<xsl:template match="/logs">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()"/>
<xsl:template match="path[substring(.,string-length()-2)!='.pi']">
<LogEntry revision="{ancestor::logentry/@revision}"
author="{preceding::author/text()}"
action="{@action}">
<xsl:copy-of select="text()"/>
</LogEntry>
</xsl:template>
</xsl:stylesheet>
+1
source to share