Yalmar
Yalmar

Reputation: 435

XSLT if parent=a and child=b then move siblings of b inside b

I have a large number of html files with the following structure:

<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
    <title>t</title>
  </head>
  <body>
    <div class="a">
      <div class="i">i</div>
      <div class="b">b1
        <div class="b1">b11</div>
        <div class="b2">b12</div>
      </div>
      <div class="j">j</div>
      <div class="b">b2
        <div class="b1">b21</div>
        <div class="b2">b22</div>
      </div>
      <div class="k">k</div>
    </div>
    <div class="x">
      <div class="i">i3</div>
      <div class="b">b3
        <div class="b1">b31</div>
        <div class="b2">b32</div>
      </div>
      <div class="j">j3</div>
    </div>
  </body>
</html>

I would like to:

  1. move all siblings of b inside b
  2. remove parent a but keep its content

Please notice that:

  1. div class="a" has not text()
  2. div class="b" can have other parents apart from div class="a", for instance div class="x"
  3. div class="a" can contain 1:N div class="b"

The output should be like the following:

<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
    <title>t</title>
  </head>
  <body>
    <div class="b">b1
      <div class="b1">b11</div>
      <div class="b2">b12</div>
      <div class="i">i</div>
      <div class="j">j</div>
      <div class="k">k</div>
    </div>
    <div class="b">b2
      <div class="b1">b21</div>
      <div class="b2">b22</div>
      <div class="i">i</div>
      <div class="j">j</div>
      <div class="k">k</div>
    </div>
    <div class="x">
      <div class="i">i3</div>
      <div class="b">b3
        <div class="b1">b31</div>
        <div class="b2">b32</div>
      </div>
      <div class="j">j3</div>
    </div>
  </body>
</html>

I am using a shell script similar to the following:

xsltproc a.xslt a.html > b.html

where a.xslt is as follows:

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

 <xsl:template match="node()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="div[@class='a']/div[@class='b']">
    <xsl:apply-templates select="@*|preceding-sibling()[not(self::div[@class='b'])]"/>
    <xsl:apply-templates select="@*|following-sibling()[not(self::div[@class='b'])]"/>
    <xsl:copy-of select="div[@class='b']"/>
 </xsl:template>

</xsl:stylesheet>

However I get an error, probably because I am not using preceding-sibling and following-sibling correctly:

xmlXPathCompOpEval: function preceding-sibling not found

Could you advise me on how to untangle this xslt please?

Upvotes: 0

Views: 51

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 116993

I believe this follows your instructions:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<!-- remove parent a but keep its content -->
<xsl:template match="div[@class='a']">
    <xsl:apply-templates/>
</xsl:template>

<!-- move all siblings of b inside b -->
<xsl:template match="div[@class='b']">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
        <xsl:copy-of select="../node()[not(self::div[@class='b'])]"/>
    </xsl:copy>
</xsl:template>

<!-- do not process siblings of b -->
<xsl:template match="node()[../div[@class='b']][not(self::div[@class='b'])]"/>

</xsl:stylesheet>

However, the result is not what you show:

<html>
   <head>
      <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
      <title>t</title>
   </head>
   <body>
      <div class="b">b1
        <div class="b1">b11</div>
         <div class="b2">b12</div>
         <div class="i">i</div>
         <div class="j">j</div>
         <div class="k">k</div>
      </div>
      <div class="b">b2
        <div class="b1">b21</div>
         <div class="b2">b22</div>
         <div class="i">i</div>
         <div class="j">j</div>
         <div class="k">k</div>
      </div>
      <div class="x">
         <div class="b">b3
        <div class="b1">b31</div>
            <div class="b2">b32</div>
            <div class="i">i3</div>
            <div class="j">j3</div>
         </div>
      </div>
   </body>
</html>

Upvotes: 1

Related Questions