nshmyrev 2008-09-21T19:43:10.819236Z

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


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


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







All Articles