1
votes

I transform multiple documents at once using XSLT. Those documents may have elements like <a:updated></a:updated> or <app:edited></app:edited> and some of them have both <a:updated></a:updated> and <app:edited></app:edited>.

In this case some of the outputed document (besides it's standard elements like title, link, content) have two times a <posted></posted> element.

The question here is how do I remove the one <posted></posted> if <app:edited> and <a:updated> are found in the same <entry></entry>?

This is the heading of XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:a="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app">

This is the template

<xsl:template match="a:updated | app:edited">
  <posted>    
    <xsl:apply-templates select="node() | @*" />
  </posted>
</xsl:template>

this is how I process it

$xproc = new XsltProcessor();
$xslt = new DomDocument;
$xslt->load('stylesheet.xslt');
$xproc->importStylesheet($xslt);

and this is basically the XML

<entry>
<id></id>
<title></title>
<content></content>
<link></link>
<a:updated></a:updated>
<app:edited></app:edited>
</entry> 
1
+1 for the good question, but please, show us the XML. Generally how and whether at all a template is selected for execution, depends on the <xsl:apply-templates> instruction that selects this template. The solution, therefore is to apply templates only on one of the occurences of a:updated , updated , app:edited - Dimitre Novatchev

1 Answers

0
votes

The correct answer depends on the source XML document, which you haven't shown.

Generally how and whether at all a template is selected for execution, depends on the <xsl:apply-templates> instruction that selects this template, so try something like this:

<xsl:apply-templates select=
  "(//*
     [self::a:updated 
     or 
      self::updated 
     or self::app:edited
     ]) 
      [1]
"/>

If possible, try to substitute the // pseudo-operator above with a more specific XPath expression, because the // is notorious for its inefficient nature.

Update: Now that the OP has posted an XML document, here is a more specific solution:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:a="a" xmlns:app="app" exclude-result-prefixes="a app"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
    <xsl:apply-templates select=
      "(/*/*
         [self::a:updated
         or
          self::updated
         or self::app:edited
         ])
          [1]
    "/>
 </xsl:template>

 <xsl:template match="a:updated | app:edited">
      <posted>
        <xsl:apply-templates select="node() | @*" />
      </posted>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document (massaged to be made well-formed):

<entry xmlns:a="a" xmlns:app="app">
    <id></id>
    <title></title>
    <content></content>
    <link></link>
    <a:updated></a:updated>
    <app:edited></app:edited>
</entry>

the wanted correct result (<posted> occurs only once in the output) is produced:

<posted/>