Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p><strong>Here is a complete XSLT 1.0 solution</strong>:</p> <pre><code>&lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ext="http://exslt.org/common" xmlns:w="w" exclude-result-prefixes="ext w"&gt; &lt;xsl:output omit-xml-declaration="yes"/&gt; &lt;xsl:strip-space elements="*"/&gt; &lt;xsl:template match="w:p"&gt; &lt;xsl:variable name="vrtfPass1"&gt; &lt;p&gt; &lt;xsl:apply-templates/&gt; &lt;/p&gt; &lt;/xsl:variable&gt; &lt;xsl:apply-templates mode="pass2" select="ext:node-set($vrtfPass1)/*"/&gt; &lt;/xsl:template&gt; &lt;xsl:template match="w:r"&gt; &lt;xsl:variable name="vrtfProps"&gt; &lt;xsl:for-each select="w:rPr/*"&gt; &lt;xsl:sort select="local-name()"/&gt; &lt;xsl:copy-of select="."/&gt; &lt;/xsl:for-each&gt; &lt;/xsl:variable&gt; &lt;xsl:call-template name="toHtml"&gt; &lt;xsl:with-param name="pProps" select= "ext:node-set($vrtfProps)/*"/&gt; &lt;xsl:with-param name="pText" select="w:t/text()"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:template&gt; &lt;xsl:template name="toHtml"&gt; &lt;xsl:param name="pProps"/&gt; &lt;xsl:param name="pText"/&gt; &lt;xsl:choose&gt; &lt;xsl:when test="not($pProps)"&gt; &lt;xsl:copy-of select="$pText"/&gt; &lt;/xsl:when&gt; &lt;xsl:otherwise&gt; &lt;xsl:element name="{local-name($pProps[1])}"&gt; &lt;xsl:call-template name="toHtml"&gt; &lt;xsl:with-param name="pProps" select= "$pProps[position()&gt;1]"/&gt; &lt;xsl:with-param name="pText" select="$pText"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:element&gt; &lt;/xsl:otherwise&gt; &lt;/xsl:choose&gt; &lt;/xsl:template&gt; &lt;xsl:template match="/*" mode="pass2"&gt; &lt;xsl:copy&gt; &lt;xsl:copy-of select="@*"/&gt; &lt;xsl:call-template name="processInner"&gt; &lt;xsl:with-param name="pNodes" select="node()"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; &lt;xsl:template name="processInner"&gt; &lt;xsl:param name="pNodes"/&gt; &lt;xsl:variable name="pNode1" select="$pNodes[1]"/&gt; &lt;xsl:if test="$pNode1"&gt; &lt;xsl:choose&gt; &lt;xsl:when test="not($pNode1/self::*)"&gt; &lt;xsl:copy-of select="$pNode1"/&gt; &lt;xsl:call-template name="processInner"&gt; &lt;xsl:with-param name="pNodes" select= "$pNodes[position()&gt;1]"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:when&gt; &lt;xsl:otherwise&gt; &lt;xsl:variable name="vbatchLength"&gt; &lt;xsl:call-template name="getBatchLength"&gt; &lt;xsl:with-param name="pNodes" select="$pNodes[position()&gt;1]"/&gt; &lt;xsl:with-param name="pName" select="name($pNode1)"/&gt; &lt;xsl:with-param name="pCount" select="1"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:variable&gt; &lt;xsl:element name="{name($pNode1)}"&gt; &lt;xsl:copy-of select="@*"/&gt; &lt;xsl:call-template name="processInner"&gt; &lt;xsl:with-param name="pNodes" select= "$pNodes[not(position()&gt;$vbatchLength)] /node()"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:element&gt; &lt;xsl:call-template name="processInner"&gt; &lt;xsl:with-param name="pNodes" select= "$pNodes[position()&gt;$vbatchLength]"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:otherwise&gt; &lt;/xsl:choose&gt; &lt;/xsl:if&gt; &lt;/xsl:template&gt; &lt;xsl:template name="getBatchLength"&gt; &lt;xsl:param name="pNodes"/&gt; &lt;xsl:param name="pName"/&gt; &lt;xsl:param name="pCount"/&gt; &lt;xsl:choose&gt; &lt;xsl:when test= "not($pNodes) or not($pNodes[1]/self::*) or not(name($pNodes[1])=$pName)"&gt; &lt;xsl:value-of select="$pCount"/&gt; &lt;/xsl:when&gt; &lt;xsl:otherwise&gt; &lt;xsl:call-template name="getBatchLength"&gt; &lt;xsl:with-param name="pNodes" select= "$pNodes[position()&gt;1]"/&gt; &lt;xsl:with-param name="pName" select="$pName"/&gt; &lt;xsl:with-param name="pCount" select="$pCount+1"/&gt; &lt;/xsl:call-template&gt; &lt;/xsl:otherwise&gt; &lt;/xsl:choose&gt; &lt;/xsl:template&gt; &lt;/xsl:stylesheet&gt; </code></pre> <p><strong>when this transformation is applied to the following XML document</strong> (based on the provided, but made more complicated to show how more edge-cases are covered):</p> <pre><code>&lt;w:p xmlns:w="w"&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;/w:rPr&gt; &lt;w:t xml:space="preserve"&gt;This is a &lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;/w:rPr&gt; &lt;w:t xml:space="preserve"&gt;bold &lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;w:i/&gt; &lt;/w:rPr&gt; &lt;w:t&gt;with a bit of italic&lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;w:i/&gt; &lt;/w:rPr&gt; &lt;w:t&gt; and some more italic&lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:i/&gt; &lt;/w:rPr&gt; &lt;w:t&gt; and just italic, no-bold&lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;/w:rPr&gt; &lt;w:t xml:space="preserve"&gt;&lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;/w:rPr&gt; &lt;w:t&gt;paragr&lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;/w:rPr&gt; &lt;w:t&gt;a&lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:rPr&gt; &lt;w:b/&gt; &lt;/w:rPr&gt; &lt;w:t&gt;ph&lt;/w:t&gt; &lt;/w:r&gt; &lt;w:r&gt; &lt;w:t xml:space="preserve"&gt; with some non-bold in it too.&lt;/w:t&gt; &lt;/w:r&gt; &lt;/w:p&gt; </code></pre> <p><strong>the wanted, correct result is produced</strong>:</p> <pre><code>&lt;p&gt;&lt;b&gt;This is a bold &lt;i&gt;with a bit of italic and some more italic&lt;/i&gt;&lt;/b&gt;&lt;i&gt; and just italic, no-bold&lt;/i&gt;&lt;b&gt;paragraph&lt;/b&gt; with some non-bold in it too.&lt;/p&gt; </code></pre> <p><strong>Explanation</strong>:</p> <ol> <li><strong>This is a two-pass transformation</strong>. The first pass is relatively simple and converts the source XML document (in our specific case) to the following:</li> </ol> <p>pass1 result (indented for readability):</p> <pre><code>&lt;p&gt; &lt;b&gt;This is a &lt;/b&gt; &lt;b&gt;bold &lt;/b&gt; &lt;b&gt; &lt;i&gt;with a bit of italic&lt;/i&gt; &lt;/b&gt; &lt;b&gt; &lt;i&gt; and some more italic&lt;/i&gt; &lt;/b&gt; &lt;i&gt; and just italic, no-bold&lt;/i&gt; &lt;b/&gt; &lt;b&gt;paragr&lt;/b&gt; &lt;b&gt;a&lt;/b&gt; &lt;b&gt;ph&lt;/b&gt; with some non-bold in it too.&lt;/p&gt; </code></pre> <p>.2. <strong>The second pass (executed in mode <code>"pass2"</code>) merges any batch of consecutive and identically named elements</strong> into a single element with that name. It recursively calls-itself on the children of the merged elements -- thus batches at any depth are merged.</p> <p>.3. <strong>Do note</strong>: We do not (and cannot) use the axes <code>following-sibling::</code> or <code>preceding-sibling</code>, because only the nodes (to be merged) at the top level are really siblings. Due to this reason we process all nodes just as a node-set.</p> <p>.4. <strong>This solution is completely generic</strong> -- it merges any batch of consecutive identically-named elements at any depth -- and no specific names are hardcoded.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload