XSLT:Break up tag structure at specic tag -


i have xml file processed using xslt 2.0 can contain tag adhering dtd fragment

<!element p (#pcdata|foo|bar|table)* > 

that input file (just 1 of possible variants)

<p>foobar   <table attr1="a" attr2="b">...</table>   <foo fooattr="foo">fdaghd</foo><bar>something</bar>sometext   <table attr1="b">...</table> </p> 

i need convert (where namespace must preserved, no usage of copy or copy-of these creates xmlns="" attributes)

<p>foobar</p> <table attr1="a" attr2="b">...</table> <p>   <foo fooattr="foo">fdaghd</foo>   <bar>something</bar>   sometext </p> <table attr1="b">...</table> 

that "split" <p>-tag whenever <table>-tag found , continue <p> after (if there children left).

please note valid input example

    <p><table attr1="a" attr2="b">...</table></p> 

which should converted into

    <table attr1="a" attr2="b">...</table> 

and valid input example

    <p>bbbb<foo>aaaa</foo></p> 

which should not converted @ all, output should be

    <p>bbbb<foo>aaaa</foo></p> 

the xslt have far includes this

<xsl:template match="p[table]">   <xsl:call-template name="split-paragraph">     <xsl:with-param name="tables" select="table"/>   </xsl:call-template> </xsl:template>  <xsl:template name="split-paragraph">   <xsl:param name="tables"/>    <xsl:if test="$tables">     <xsl:for-each select="$tables[1]">       <xsl:if test="not(preceding-sibling::node//table)">           <p><xsl:apply-templates select="preceding-sibling::node()[not(table)]"/></p>       </xsl:if>        <xsl:apply-templates select="."/>       <xsl:if test="not(following-sibling::node()//table)">           <p><xsl:apply-templates select="following-sibling::node[. &lt;&lt; following-sibling::node()[not(name()='table')][1]]"/></p>       </xsl:if>        <xsl:call-template name="split-paragraph">         <xsl:with-param name="tables"                         select="$tables[position() > 1]"/>       </xsl:call-template>     </xsl:for-each>   </xsl:if> </xsl:template>  <xsl:template match="table">   <xsl:element name="table">     <xsl:apply-templates select="attribute()"/>     <xsl:apply-templates select="node()"/>   </xsl:element> </xsl:template>  <xsl:template match="element()"><xsl:copy/></xsl:template> 

which applied to

<p>foo   <table attr1="gazonk"><a>bar</a></table>   <bar>xyzzy</bar>   <table attr2="2"><b>fie</b></table>   shfjkdashndk </p> 

generates

<p>foo</p> <table attr1="gazonk">   <a>bar</a> </table> <p> </p> <p>foo   <table attr1="gazonk">     <a>bar</a>   </table>   <bar>xyzzy</bar> </p> <table attr2="2">   <b>fie</b> </table> <p>shfjkdashndk</p> 

which not desired output. want this

<p>foo</p> <table attr1="gazonk">   <a>bar</a> </table> <p><bar>xyzzy</bar></p> <table attr2="2">   <b>fie</b> </table> <p>shfjkdashndk</p> 

if using xslt 1.0, can treat grouping problem. grouping not-table elements together, grouping being done on number of table elements precede nodes group. using muenchian grouping, define key so

<xsl:key name="group" match="p/node()[not(self::table)]" use="concat(generate-id(..), '-', count(preceding-sibling::table))" /> 

(the generate-id() here cope multiple p elements in xml)

you start off having template match p elements, skip on element, them select children, using "mode" indicate special processing (and avoid having 2 templates match same node)

<xsl:template match="p">     <xsl:apply-templates mode="group" /> </xsl:template> 

you need template match first non-table element occurs in each group, add p tag around , other items in group, so

<xsl:template match="node()[generate-id() = generate-id(key('group', concat(generate-id(..), '-', count(preceding-sibling::table)))[1])]" mode="group">     <p>         <xsl:apply-templates select="key('group', concat(generate-id(..), '-', count(preceding-sibling::table)))"/>     </p> </xsl:template> 

(note, xslt using identity template output other nodes output)

matching table more straight-forward:

 <xsl:template match="table" mode="group">      <xsl:apply-templates select="." /> </xsl:template> 

finally, need template match other nodes not picked previous 2 templates, ignore them (as previous "group" template have outputted them. use of "mode" becomes apparent)

<xsl:template match="node()" mode="group" />  

try xslt

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="1.0">     <xsl:output indent="yes"/>     <xsl:strip-space elements="*"/>      <xsl:key name="group" match="p/node()[not(self::table)]" use="concat(generate-id(..), '-', count(preceding-sibling::table))" />      <xsl:template match="p">         <xsl:apply-templates mode="group" />     </xsl:template>      <xsl:template match="node()[generate-id() = generate-id(key('group', concat(generate-id(..), '-', count(preceding-sibling::table)))[1])]" mode="group">         <p>             <xsl:apply-templates select="key('group', concat(generate-id(..), '-', count(preceding-sibling::table)))"/>         </p>     </xsl:template>       <xsl:template match="table" mode="group">          <xsl:apply-templates select="." />     </xsl:template>      <xsl:template match="node()" mode="group" />       <xsl:template match="@*|node()" >         <xsl:copy>             <xsl:apply-templates select="@*|node()"/>         </xsl:copy>     </xsl:template> </xsl:stylesheet> 

edit: cope namespaces, in xslt 1.0, have replace instances of p in xpath expressions *[local-name() = 'p'], , table. going have use xsl:element create new p element same namespace.

try xslt:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="1.0">     <xsl:output indent="yes"/>     <xsl:strip-space elements="*"/>      <xsl:key name="group" match="*[local-name() = 'p']/node()[not(self::*[local-name() = 'table'])]" use="concat(generate-id(..), '-', count(preceding-sibling::*[local-name() = 'table']))" />      <xsl:template match="*[local-name() = 'p']">         <xsl:apply-templates mode="group" />     </xsl:template>      <xsl:template match="node()[generate-id() = generate-id(key('group', concat(generate-id(..), '-', count(preceding-sibling::*[local-name() = 'table'])))[1])]" mode="group">         <xsl:element name="p" namespace="{namespace-uri(..)}">             <xsl:apply-templates select="key('group', concat(generate-id(..), '-', count(preceding-sibling::*[local-name() = 'table'])))"/>         </xsl:element>      </xsl:template>       <xsl:template match="*[local-name() = 'table']" mode="group">          <xsl:apply-templates select="." />     </xsl:template>      <xsl:template match="node()" mode="group" />       <xsl:template match="@*|node()" >         <xsl:copy>             <xsl:apply-templates select="@*|node()"/>         </xsl:copy>     </xsl:template> </xsl:stylesheet> 

edit 2.0

here xslt 2.0 solution, can use xsl:for-each-group group elements. in case, grouping adjacent elements together, depending on whether table or not.

also not use of wildcards in checking namespaces (which cope whether namespaces present or not)

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="2.0">     <xsl:output indent="yes"/>     <xsl:strip-space elements="*"/>      <xsl:template match="*:p">         <xsl:for-each-group select="node()" group-adjacent="boolean(self::*:table)">             <xsl:choose>                 <xsl:when test="self::*:table">                     <xsl:apply-templates select="current-group()" />                 </xsl:when>                 <xsl:otherwise>                     <xsl:element name="p" namespace="{namespace-uri(..)}">                         <xsl:apply-templates select="current-group()" />                     </xsl:element>                 </xsl:otherwise>             </xsl:choose>         </xsl:for-each-group>     </xsl:template>      <xsl:template match="@*|node()" >         <xsl:copy>             <xsl:apply-templates select="@*|node()"/>         </xsl:copy>     </xsl:template> </xsl:stylesheet> 

Comments

Popular posts from this blog

database - VFP Grid + SQL server 2008 - grid not showing correctly -

jquery - Set jPicker field to empty value -

.htaccess - htaccess convert request to clean url and add slash at the end of the url -