Jump to content

[xslt] Group Tags To Prioritize The Xml File


sacha74
 Share

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...

Link to comment
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>

Link to comment
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.

Link to comment
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. :)

Link to comment
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.

Link to comment
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?

Link to comment
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.

Link to comment
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
Link to comment
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
Link to comment
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.

Link to comment
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?

Link to comment
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
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...