2010-11-03 3 views
0

Je voudrais m'excuser pour le mauvais titre - je ne savais vraiment pas comment l'exprimer mieux. Je travaille actuellement sur un script XSLT 1.0 (en utilisant xsltproc) qui transforme un simple format XML en une représentation de texte adaptée à la consommation par un générateur PDF. Dans mon format XML, il n'y a que trois éléments: <book>, <chapter> et <section>. Cependant, en raison d'une caractéristique désagréable de la DTD, j'ai du mal à écrire un script XSLT approprié pour transformer le document. Voici la DTD qui décrit leur relation:Comment puis-je faire en sorte que mon modèle XSLT gère les éléments parents manquants?

<!ELEMENT book ((chapter|section)*)> 
<!ELEMENT chapter (section*)> 
<!ELEMENT section (#PCDATA)> 

Voici la ma feuille de style XSLT qui effectue la traduction:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="text" encoding="iso-8859-1"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="section"> 
    <xsl:if test="not(preceding-sibling::section)">@BeginSections&#xa;</xsl:if> 
    <xsl:text>@Section @Begin&#xa;</xsl:text> 
    <xsl:apply-templates/> 
    <xsl:text>@End @Section&#xa;</xsl:text> 
    <xsl:if test="not(following-sibling::section)">@EndSections&#xa;</xsl:if> 
</xsl:template> 

<xsl:template match="chapter"> 
    <xsl:if test="not(preceding-sibling::chapter)">@BeginChapters&#xa;</xsl:if> 
    <xsl:text>@Chapter @Begin&#xa;</xsl:text> 
    <xsl:apply-templates/> 
    <xsl:text>@End @Chapter&#xa;</xsl:text> 
    <xsl:if test="not(following-sibling::chapter)">@EndChapters&#xa;</xsl:if> 
</xsl:template> 

<xsl:template match="/book"> 
    <xsl:text>@Book @Begin&#xa;</xsl:text> 
    <xsl:apply-templates/> 
    <xsl:text>@End @Book&#xa;</xsl:text> 
</xsl:template> 
</xsl:stylesheet> 

Maintenant, voici la partie délicate et ma question: la DTD permet d'avoir <section> comme les enfants directs de <book>. Cependant, je dois encore donner la même sortie que si cet élément /book/section était réellement /book/chapter/section.

Ainsi par exemple: <book><section/><chapter/></book> devient (je dentelées la sortie pour une meilleure lisibilité)

@Book @Begin 
    @BeginChapters 
    @Chapter @Begin 
    @BeginSections 
    @Section @Begin 
    @End @Section 
    @EndSections 
    @End @Chapter 
    @Chapter @Begin 
    @End @Chapter 
    @EndChapters 
@End @Book 

Donc ce que je voudrais faire est d'ajuster mon script XSLT de sorte que le modèle « section » appelle en quelque sorte aussi modèle 'chapter' dans le cas où l'élément donné <section> ne se trouve pas dans <chapter>. Comment pourrais-je faire ça?

Pour ce que ça vaut, voici un autre exemple. Plusieurs éléments <section> qui ne sont pas déjà dans un <chapter> doivent être fusionnés en un. Par conséquent, <book><section/><section/><section/><chapter/><section/></book> produit une sortie pour trois chapitres (le premier a trois sections, le second n'en a pas, le troisième a une section).

+0

Impossible d'utiliser une forme de , utilisez maths soit chapitre soit section. Après avoir apparié l'un d'entre eux, continuez à faire correspondre une ou plusieurs sections – frayser

Répondre

2

Cette feuille de style:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text" encoding="iso-8859-1"/> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="chapter|section" mode="chapter" name="makeChapter"> 
     <xsl:text> @Chapter @Begin&#xa;</xsl:text> 
     <xsl:apply-templates select="self::chapter/node()[1]| 
            self::section" 
          mode="section"/> 
     <xsl:text> @End @Chapter&#xa;</xsl:text> 
     <xsl:apply-templates 
      select="self::chapter/following-sibling::node()[1]| 
        self::section/following-sibling::chapter[1] " 
      mode="chapter"/> 
    </xsl:template> 
    <xsl:template match="section" mode="makeSection" name="makeSection"> 
     <xsl:text> @Section @Begin&#xa;</xsl:text> 
     <xsl:apply-templates/> 
     <xsl:text> @End @Section&#xa;</xsl:text> 
     <xsl:apply-templates select="following-sibling::node()[1]/self::section" 
          mode="makeSection"/> 
    </xsl:template> 
    <xsl:template match="section" mode="section"> 
     <xsl:text> @BeginSections&#xa;</xsl:text> 
     <xsl:call-template name="makeSection"/> 
     <xsl:text> @EndSections&#xa;</xsl:text> 
    </xsl:template> 
    <xsl:template match="book/chapter|book/section"> 
     <xsl:text> @BeginChapters&#xa;</xsl:text> 
     <xsl:call-template name="makeChapter"/> 
     <xsl:text> @EndChapters&#xa;</xsl:text> 
    </xsl:template> 
    <xsl:template match="book"> 
     <xsl:text>@Book @Begin&#xa;</xsl:text> 
     <xsl:apply-templates select="node()[1]"/> 
     <xsl:text>@End @Book&#xa;</xsl:text> 
    </xsl:template> 
</xsl:stylesheet> 

Sortie:

@Book @Begin 
    @BeginChapters 
    @Chapter @Begin 
    @BeginSections 
    @Section @Begin 
    @End @Section 
    @EndSections 
    @End @Chapter 
    @Chapter @Begin 
    @End @Chapter 
    @EndChapters 
@End @Book 

Note: traversée à grain fin, regroupant des sections/livres adjacents en un chapitre.

Modifier: Correction du processus de fratrie suivant pour Chapters.

Avec cette entrée:

<book> 
    <section/> 
    <section/> 
    <section/> 
    <chapter/> 
    <section/> 
</book> 

Sortie:

@Book @Begin 
    @BeginChapters 
    @Chapter @Begin 
    @BeginSections 
    @Section @Begin 
    @End @Section 
    @Section @Begin 
    @End @Section 
    @Section @Begin 
    @End @Section 
    @EndSections 
    @End @Chapter 
    @Chapter @Begin 
    @End @Chapter 
    @Chapter @Begin 
    @BeginSections 
    @Section @Begin 
    @End @Section 
    @EndSections 
    @End @Chapter 
    @EndChapters 
@End @Book 

Modifier: modèles mieux nommés à comprendre.

Remarque: Cinq règles: book règle "ouvre" un livre et traite le premier enfant; book/section|book/chapter règle (toujours le premier parce que le transversal à grain fin) "ouvre" des chapitres de livre, appelle makeChapter; makeChapter règle, « ouvre » un chapitre, un processus premier enfant si le contexte est chapter ou auto si le contexte est section aussi bien en mode section, processus suivant frères et soeurs si le contexte est chapter ou après chapter si le contexte est section en mode chapter (ce qui signifie chapitre suivant); section règle en mode section (parce que le processus nœud par nœud, il sera toujours correspondre à la première sections pour adjacents sections) "ouvre" sections de chapitre un appel makeSection règle; makeSection règle "ouvre" une section un processus childs, puis traiter le frère suivant section en mode makeSection (cette règle).

+0

+1: Ah! Cela a l'air intéressant. Je vais essayer de comprendre un peu et voir si je peux l'étendre. –

+0

@Frerich Raabe: Explication ajoutée. –

0

vous devez d'abord envelopper les sections orphelines dans un seul chapitre ..

nous créons une variable pour ce pour maintenir les éléments enveloppés

<xsl:variable name="orphan"> 
    <chapter> 
    <xsl:for-each select="/book/section"> 
     <xsl:copy-of select="." /> 
    </xsl:for-each> 
    </chapter> 
</xsl:variable> 

puis lorsque vous appliquez les modèles à l'intérieur du correspondant /book modèle que vous devez aussi utiliser cette variable nouvellement créée

<xsl:apply-templates select="chapter|exslt:node-set($orphan)"/> 

et utiliser le exslt vous devez ajouter le espace de noms

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common"> 

résultat final est

<?xml version="1.0" encoding="ISO-8859-1"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common"> 
<xsl:output method="html" encoding="iso-8859-1"/> 
<xsl:strip-space elements="*"/> 

<xsl:variable name="orphan"> 
    <chapter> 
    <xsl:for-each select="/book/section"> 
     <xsl:copy-of select="." /> 
    </xsl:for-each> 
    </chapter> 
</xsl:variable> 

<xsl:template match="section"> 
    <xsl:if test="not(preceding-sibling::section)">@BeginSections&#xa;</xsl:if> 
    <xsl:text>@Section @Begin&#xa;</xsl:text> 
    <xsl:apply-templates/> 
    <xsl:text>@End @Section&#xa;</xsl:text> 
    <xsl:if test="not(following-sibling::section)">@EndSections&#xa;</xsl:if> 
</xsl:template> 

<xsl:template match="chapter"> 
    <xsl:if test="not(preceding-sibling::chapter)">@BeginChapters&#xa;</xsl:if> 
    <xsl:text>@Chapter @Begin&#xa;</xsl:text> 
    <xsl:apply-templates/> 
    <xsl:text>@End @Chapter&#xa;</xsl:text> 
    <xsl:if test="not(following-sibling::chapter)">@EndChapters&#xa;</xsl:if> 
</xsl:template> 

<xsl:template match="/book"> 
    <xsl:text>@Book @Begin&#xa;</xsl:text> 
    <xsl:apply-templates select="chapter|exslt:node-set($orphan)"/> 
    <xsl:text>@End @Book&#xa;</xsl:text> 
</xsl:template> 
</xsl:stylesheet> 
Questions connexes