Jump to content
sacha74

[xslt] Group Tags To Prioritize The Xml File

Recommended Posts

Hello, I have 2 problems with an xml file:1. the original xml has no structure because all the tags (except the root element) are at the same level. What I want to do is to insert a tag <toto>, whose children would be one <FM1Titre> and all the tags that follow it until the next tag <FM1Titre>. 2. I would like to report the content of the tag FM1Titre as the name of the tag that I have mentionned above. Here is a part of the xml file: <XML> <FM1Titre>Description </FM1Titre> <Bloc-paragraphe>La table mère permet de paramétrer les tables. </Bloc-paragraphe> <Bloc-paragraphe>Toute nouvelle table doit être créée en premier lieu dans la table mère.</Bloc-paragraphe> <FM1Titre>Détails techniques </FM1Titre> ...</XML> What I would like to obtain after transformation : <XML> <Description> <FM1Titre>Description </FM1Titre> <Bloc-paragraphe>La table mère permet de paramétrer les tables. </Bloc-paragraphe> <Bloc-paragraphe>Toute nouvelle table doit être créée en premier lieu dans la table mère.</Bloc-paragraphe> </Description> <Détails techniques> <FM1Titre>Détails techniques </FM1Titre> </Détails techniques> ...</XML> My researches have been unsuccessful (research on the net, on proposed solutions on the forum) and I begin to think that it is maybe impossible to do that with XSLT 1.0.I use to find a solution to problem of that kind but this time I do not manage to find an answer by myself, so if anyone could have a solution or a track, please help me...

Share this post


Link to post
Share on other sites

What you want is possible with XSLT 1.0, but it's certainly not trivial. This collection of Grouping related mailing list discussions by Dave Pawson may have some clues to the solution.Here's a possible solution, based on your XML and this particular thingy:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:template match="XML">	<xsl:copy>	  <xsl:apply-templates select="FM1Titre" mode="walker"/>	</xsl:copy>  </xsl:template>  <xsl:template match="title" mode="walker">	<Description>	  <xsl:apply-templates select="."/>	  <xsl:apply-templates select="following-sibling::*[1]" mode="walker"/>	</Description>  </xsl:template>  <xsl:template match="*" mode="walker">	<xsl:apply-templates select="."/>	<xsl:if test="not(following-sibling::*[1]/self::FM1Titre)">	  <xsl:apply-templates select="following-sibling::*[1]" mode="walker"/>	</xsl:if>  </xsl:template>  <xsl:template match="@* | node()">	<xsl:copy>	  <xsl:apply-templates select="@* | node()"/>	</xsl:copy>  </xsl:template></xsl:stylesheet>

Share this post


Link to post
Share on other sites

You can use sibling recursion with XSLT 1.0.This input

<XML><FM1Titre>Description </FM1Titre><Bloc-paragraphe>La table mère permet de paramétrer les tables. </Bloc-paragraphe><Bloc-paragraphe>Toute nouvelle table doit être créée en premier lieu dans la table mère.</Bloc-paragraphe><FM1Titre>Détails techniques </FM1Titre><foo>foo 2, 1</foo><bar>bar 2, 1</bar><FM1Titre>whatever</FM1Titre><foo>foo 3, 1</foo><foo>foo 3, 2</foo><bar>bar 3,1 </bar></XML>

is transformed with this stylesheet

<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0">    <xsl:output indent="yes"/>    <xsl:template match="/XML">	<xsl:copy>	  <xsl:apply-templates select="FM1Titre[1]"/>	</xsl:copy>  </xsl:template>    <xsl:template match="FM1Titre">	<xsl:element name="{translate(normalize-space(.), ' ', '_')}">	  <xsl:apply-templates select="following-sibling::*[1][not(self::FM1Titre)]"/>	</xsl:element>	<xsl:apply-templates select="following-sibling::FM1Titre[1]"/>  </xsl:template>    <xsl:template match="/XML/*[not(self::FM1Titre)]">	<xsl:copy>	  <xsl:apply-templates select="@* | node()"/>	</xsl:copy>	<xsl:apply-templates select="following-sibling::*[1][not(self::FM1Titre)]"/>  </xsl:template></xsl:stylesheet>

to the following result:

<XML>   <Description>	  <Bloc-paragraphe>La table mère permet de paramétrer les tables. </Bloc-paragraphe>	  <Bloc-paragraphe>Toute nouvelle table doit être créée en premier lieu dans la table mère.</Bloc-paragraphe>   </Description>   <Détails_techniques>	  <foo>foo 2, 1</foo>	  <bar>bar 2, 1</bar>   </Détails_techniques>   <whatever>	  <foo>foo 3, 1</foo>	  <foo>foo 3, 2</foo>	  <bar>bar 3,1 </bar>   </whatever></XML>

Note that your element contents contained characters like spaces that are not allowed in XML element names so I had to implement a slight change to your requirement.

Share this post


Link to post
Share on other sites

Hello,Again it's me. I have another problem.In the xml file I also have tags <FM2Titre> at the same level but when I tried to apply the nearly same code to this tag, I have an error "Expecetd QName" for the line <xsl:element name="{translate(normalize-space(.), ' ', '_')}">. So I tried to make another xslt, which first transforms the tag <FM2Titre> en <FM1Titre> and then to apply in a second time the other xslt which works after but it does not work. Once again an error "Expecetd QName" occurs. :) Perhaps it would be clearer with an example.From this code:<XML><FM1Titre>Description </FM1Titre><Bloc-paragraphe>La table mère permet de paramétrer les tables. </Bloc-paragraphe><Bloc-paragraphe>Toute nouvelle table doit être créée en premier lieu dans la table mère.</Bloc-paragraphe><FM1Titre>Détails techniques </FM1Titre><foo>foo 2, 1</foo><bar>bar 2, 1</bar><FM1Titre>whatever</FM1Titre><foo>foo 3, 1</foo><foo>foo 3, 2</foo><bar>bar 3,1 </bar><FM2Titre>clé d'accès</FM2Titre><foo>foo xxxxx</foo><foo>foo yyyy</foo><bar>bar ddd </bar></XML>I would like to have:<XML> <Description> <Bloc-paragraphe>La table mère permet de paramétrer les tables. </Bloc-paragraphe> <Bloc-paragraphe>Toute nouvelle table doit être créée en premier lieu dans la table mère.</Bloc-paragraphe> </Description> <Détails_techniques> <foo>foo 2, 1</foo> <bar>bar 2, 1</bar> </Détails_techniques> <whatever> <foo>foo 3, 1</foo> <foo>foo 3, 2</foo> <bar>bar 3,1 </bar> </whatever> <Clé_d'accès> <foo>foo 3, 1</foo> <foo>foo 3, 2</foo> <bar>bar 3,1 </bar> </Clé_d'accès></XML>Can you try to explain me why it does not work, where am I mistaken ?Thanks for any help I can have. :)

Share this post


Link to post
Share on other sites

Clé_d'accès is not allowed as an XML name, you will need to replace the single quote with an allowed character.

<xsl:element name="{translate(normalize-space(.), " '", '__')}">

would replace the single quote and a space with an underscore '_' character.If you use XSLT 2.0 then you could also use more complex replacements using the replace function if that is needed respectively if you expect other characters that are not allowed in XML names.

Share this post


Link to post
Share on other sites

I'm sorry but once again the same error occurs "Expected QName" for the code name="{translate(normalize-space(.), " '", '__')}">.Code to transform <FM2Titre> (nearly the same as the code to transform the <FM1Titre> tag):<xsl:template match="/XML"> <xsl:copy> <xsl:apply-templates select="FM2Titre[1]"/> </xsl:copy> </xsl:template> <xsl:template match="FM2Titre"> <xsl:element name="{translate(normalize-space(.), " '", '__')}"> <xsl:apply-templates select="following-sibling::*[1][not(self::FM2Titre)]"/> </xsl:element> <xsl:apply-templates select="following-sibling::FM2Titre[1]"/> </xsl:template> <xsl:template match="/XML/*[not(self::FM2Titre)]"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> <xsl:apply-templates select="following-sibling::*[1][not(self::FM2Titre)]"/> </xsl:template>What I can't understand is why the code transforms <FM1Titre> and why not it does not transform <FM2Titre>, even in another xslt file? Indeed, at the beginning, I believed that the rules did not manage to follow one another. So I made one xslt file for one transformation. But even separated, the code transforms <FM1Titre> and it does not transform <FM2Titre>.What is the thing I missed?

Share this post


Link to post
Share on other sites

You will need to provide more details, in a particular a minimal but complete XML input document and stylesheet that creates the error.When I run

<xsl:stylesheet  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  version="1.0">    <xsl:output indent="yes"/><xsl:template match="/XML"><xsl:copy><xsl:apply-templates select="FM2Titre[1]"/></xsl:copy></xsl:template><xsl:template match="FM2Titre"><xsl:element name="{translate(normalize-space(.), " '", '__')}"><xsl:apply-templates select="following-sibling::*[1][not(self::FM2Titre)]"/></xsl:element><xsl:apply-templates select="following-sibling::FM2Titre[1]"/></xsl:template><xsl:template match="/XML/*[not(self::FM2Titre)]"><xsl:copy><xsl:apply-templates select="@* | node()"/></xsl:copy><xsl:apply-templates select="following-sibling::*[1][not(self::FM2Titre)]"/></xsl:template></xsl:stylesheet>

against the input

<XML><FM1Titre>Description </FM1Titre><Bloc-paragraphe>La table mère permet de paramétrer les tables. </Bloc-paragraphe><Bloc-paragraphe>Toute nouvelle table doit être créée en premier lieu dans la table mère.</Bloc-paragraphe><FM1Titre>Détails techniques </FM1Titre><foo>foo 2, 1</foo><bar>bar 2, 1</bar><FM1Titre>whatever</FM1Titre><foo>foo 3, 1</foo><foo>foo 3, 2</foo><bar>bar 3,1 </bar><FM2Titre>clé d'accès</FM2Titre><foo>foo xxxxx</foo><foo>foo yyyy</foo><bar>bar ddd </bar></XML>

then the resulting XML document is as follows:

<?xml version="1.0" encoding="utf-8"?><XML>   <clé_d_accès>	  <foo>foo xxxxx</foo>	  <foo>foo yyyy</foo>	  <bar>bar ddd </bar>   </clé_d_accès></XML>

I realize that is not complete result you want but it certainly does not generate the error you describe.Generally such errors occur when you are trying to create an element with xsl:element where the name you are trying to construct is not an allowed XML name. The translate I wrote converts two characters that are not allowed, the space and the single quote, to an underscore. If your input has other not allowed characters then you need to adapt the translate to list more characters and their replacement. Or you can use replace if you use XSLT 2.0.

Share this post


Link to post
Share on other sites

Finally the code you gave me works (you were right) but the problem was that there were other special characters: (, ).I have found the code to translate those characters : &lpar; &rpar;.But when I try to join those new characters with the others, an error occurs because I probably use an incorrect syntax in the xslt code.So this code works:<xsl:element name="{translate(normalize-space(.), " '", '__')}"> whereas this code does not work:<xsl:element name="{translate(normalize-space(.), " &lpar; &lpar; '", '____')}">.I had the idea to do the translation in a variable but I can't it would change those characters even in the text. That is why I have to make all the translations in the line of the tag creation.I wonder if you or anyone else could help me.Thanks in advance.

Edited by sacha74

Share this post


Link to post
Share on other sites

A space is only needed once to target the space character. Also, instead of using named entities like lpar and rpar, you should use their numeric equivalents or the characters themselves. If it's a comma, then

<xsl:element name="{translate(normalize-space(.), " '#44;", '___')}">

(add a "&" before "#44;". I've removed it because this board interprets numeric entities)

Edited by boen_robot

Share this post


Link to post
Share on other sites

Hello,I'm back once again. I try to group two transformations in my xslt file but I have the impression that my xml editor don't manage to go on the second template after doing the first one.I want to treat tables. The problem is that I have 4 tables which follows one another and when i asked for the transformation just one table is transfromed and the following three have disappeared.Here is my xslt:<xsl:template match="/XML" priority="1"> <xsl:copy> <xsl:apply-templates select="FM1Titre[1]"/> </xsl:copy> </xsl:template> <xsl:variable name="Schar"> '’():/éèà,"</xsl:variable> <xsl:variable name="Rchar">_______eea__</xsl:variable> <xsl:template match="FM1Titre"> <xsl:element name="{translate(normalize-space(.), $Schar, $Rchar)}"> <xsl:apply-templates select="following-sibling::*[1][not(self::FM1Titre)]"/> </xsl:element> <xsl:apply-templates select="following-sibling::FM1Titre[1]"/> </xsl:template> <xsl:template match="/XML/*[not(self::FM1Titre)]"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> <xsl:apply-templates select="following-sibling::*[1][not(self::FM1Titre)]"/> </xsl:template> <xsl:template match="//TABLE" priority="2"> <xsl:value-of select="TABLE"/> <table> <tgroup cols="6"> <colspec colname = "1" colnum = "1" colwidth = "1.85cm"/> <colspec colname = "2" colnum = "2" colwidth = "10.9cm"/> <colspec colname = "3" colnum = "3" colwidth = "0.65cm"/> <colspec colname = "4" colnum = "4" colwidth = "0.65cm"/> <colspec colname = "5" colnum = "5" colwidth = "1cm"/> <colspec colname = "6" colnum = "6" colwidth = "1cm"/> <thead> <row> <entry colname="1"><xsl:value-of select="ROW/TH[1]/Tableau-intitulé"/></entry> <entry colname="2"><xsl:value-of select="ROW/TH[2]/Tableau-intitulé"/></entry> <entry colname="3"><xsl:value-of select="ROW/TH[3]/Tableau-intitulé"/></entry> <entry colname="4"><xsl:value-of select="ROW/TH[4]/Tableau-intitulé"/></entry> <entry colname="5"><xsl:value-of select="ROW/TH[5]/Tableau-intitulé"/></entry> <entry colname="6"><xsl:value-of select="ROW/TH[6]/Tableau-intitulé"/></entry> </row> </thead> <tbody> <xsl:for-each select="ROW[position () > 1]"> <row> <entry colname="1"><CELL><xsl:apply-templates select="CELL[1]/node()"/></CELL></entry> <entry colname="2"><CELL><xsl:apply-templates select="CELL[2]/node()"/></CELL></entry> <entry colname="3"><CELL><xsl:apply-templates select="CELL[3]/node()"/></CELL></entry> <entry colname="4"><CELL><xsl:apply-templates select="CELL[4]/node()"/></CELL></entry> <entry colname="5"><CELL><xsl:apply-templates select="CELL[5]/node()"/></CELL></entry> <entry colname="6"><CELL><xsl:apply-templates select="CELL[6]/node()"/></CELL></entry> </row> </xsl:for-each> </tbody> </tgroup> </table> </xsl:template>What am I doing wrong in the second template and that would explain that only one table on 4 is recovered ?Thanks in advance for all the help I could find.

Share this post


Link to post
Share on other sites

Consider to post your XML input and the stylesheet. Both should be as small as possible to demonstrate the problem but should be complete as well to allow us to understand and reproduce the problem. It will also help if you post the output you expect and the output you get.

Share this post


Link to post
Share on other sites
My input :<XML><FM1Titre>description</FM1Titre><p>tata</p> <TABLE>....</TABLE><FM1Titre>Codes retenus</FM1Titre><TABLE><ROW><TH><Tableau-intitulé>totot</Tableau-intitulé></TH><TH><Tableau-intitulé> [...]<ROW><CELL><Tableau-cellule-centrée-gras>ACT(802)</Tableau-cellule-centrée-gras></CELL><CELL> [...]</TABLE><TABLE>....</TABLE><TABLE>....</TABLE><TABLE>....</TABLE></XML>

My xslt file is rather long so perhaps you can take the one in my previous post. Tell me if it is a problem.

My output :<XML><description> <p>tata</p> <TABLE>....</TABLE></description><Codes_retenus><TABLE></TABLE></Codes_retenus></XML>

In my output three tables are missing. I have tried with <xsl:for-each> but it doesn't work better. Moreover the transformation of the table works well when it is in an other xslt.So I'd like to put these two xslt in just one file. Is it possible?

Share this post


Link to post
Share on other sites

If you want to use sibling recursion but want to transform TABLE elements too then you can write a template for the TABLE elements as you have done but you will need to ensure inside of that template that the sibling recursion continues e.g.

<xsl:template match="TABLE" priority="2"><table><tgroup cols="6"><colspec colname = "1" colnum = "1" colwidth = "1.85cm"/><colspec colname = "2" colnum = "2" colwidth = "10.9cm"/><colspec colname = "3" colnum = "3" colwidth = "0.65cm"/><colspec colname = "4" colnum = "4" colwidth = "0.65cm"/><colspec colname = "5" colnum = "5" colwidth = "1cm"/><colspec colname = "6" colnum = "6" colwidth = "1cm"/><thead><row><entry colname="1"><xsl:value-of select="ROW/TH[1]/Tableau-intitulé"/></entry><entry colname="2"><xsl:value-of select="ROW/TH[2]/Tableau-intitulé"/></entry><entry colname="3"><xsl:value-of select="ROW/TH[3]/Tableau-intitulé"/></entry><entry colname="4"><xsl:value-of select="ROW/TH[4]/Tableau-intitulé"/></entry><entry colname="5"><xsl:value-of select="ROW/TH[5]/Tableau-intitulé"/></entry><entry colname="6"><xsl:value-of select="ROW/TH[6]/Tableau-intitulé"/></entry></row></thead><tbody><xsl:for-each select="ROW[position () > 1]"><row><entry colname="1"><CELL><xsl:apply-templates select="CELL[1]/node()"/></CELL></entry><entry colname="2"><CELL><xsl:apply-templates select="CELL[2]/node()"/></CELL></entry><entry colname="3"><CELL><xsl:apply-templates select="CELL[3]/node()"/></CELL></entry><entry colname="4"><CELL><xsl:apply-templates select="CELL[4]/node()"/></CELL></entry><entry colname="5"><CELL><xsl:apply-templates select="CELL[5]/node()"/></CELL></entry><entry colname="6"><CELL><xsl:apply-templates select="CELL[6]/node()"/></CELL></entry></row></xsl:for-each></tbody></tgroup></table><xsl:apply-templates select="following-sibling::*[1][not(self::FM1Titre)]"/></xsl:template>

Edited by Martin Honnen

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

×
×
  • Create New...