2017-06-05 5 views
1

J'ai un document XHTML provenant d'une sortie ePub de base que j'essaie de convertir en un document XML structuré. Le format de celui-ci ne devrait pas être trop fou en général et se présente comme suit:XHTML vers XML structuré avec XSLT 1.0

<?xml version="1.0" encoding="utf-8"?> 
<html> 
<body> 
    <h1>Topic 1</h1> 
    <p>1.0.1</p> 
    <p>1.0.2</p> 

    <h2>Subtopic 1.1</h2> 
    <p>1.1.1</p> 
    <p>1.1.2</p> 

    <h2>Subtopic 1.2</h2> 
    <p>1.2.1</p> 
    <p>1.2.2</p> 

    <h1>Topic 2</h1> 
    <p>2.0.1</p> 
    <p>2.0.2</p> 

    <h2>Subtopic 2.1</h2> 
    <p>2.1.1</p> 
    <p>2.1.2</p> 

    <h2>Subtopic 2.2</h2> 
    <p>2.2.1</p> 
    <p>2.2.2</p> 
</body> 
</html> 

Idéalement, je voudrais convertir en un code structuré, basé sur les h1, h2, ... tags . La substance après la première h1, mais avant la seconde doit être contenue dans son propre conteneur, et la substance dans la deuxième h1 à la fin du document à l'intérieur de son propre. De même, la substance entre les h2 devrait également aller dans un conteneur, l'imbriquant ainsi. La sortie devrait être quelque chose comme ceci:

<Root> 
    <Topic> 
     <Title>Topic 1</Title> 
     <Paragraph>1.0.1</Paragraph> 
     <Paragraph>1.0.2</Paragraph> 
     <Topic> 
     <Title>Subtopic 1.1</Title> 
     <Paragraph>1.1.1</Paragraph> 
     <Paragraph>1.1.2</Paragraph> 
     </Topic> 
     <Topic> 
     <Title>Subtopic 1.2</Title> 
     <Paragraph>1.2.1</Paragraph> 
     <Paragraph>1.2.2</Paragraph> 
     </Topic> 
    </Topic> 
    <Topic> 
     <Title>Topic 2</Title> 
     <Paragraph>2.0.1</Paragraph> 
     <Paragraph>2.0.2</Paragraph> 
     <Topic> 
     <Title>Subtopic 2.1</Title> 
     <Paragraph>2.1.1</Paragraph> 
     <Paragraph>2.1.2</Paragraph> 
     </Topic> 
     <Topic> 
     <Title>Subtopic 2.2</Title> 
     <Paragraph>2.2.1</Paragraph> 
     <Paragraph>2.2.2</Paragraph> 
     </Topic> 
    </Topic> 
</Root> 

Même si l'exemple ne se compose que des balises p, il peut également contenir DIV, et d'autres éléments, donc ne comptez pas être un seul nœud. Il doit être assez générique pour ne pas se soucier de ce qui se trouve entre les balises d'en-tête. Je suis familier avec le groupement Muenchian, mais c'est un peu complexe d'une situation pour moi. Je l'ai essayé en utilisant les touches comme ceci:

<xsl:key name="kHeaders1" match="*[not(self::h1)]" use="generate-id(preceding-sibling::h1[1])"/> 

<xsl:template match="h1"> 
    <Topic> 
    <Title><xsl:apply-templates /></Title> 
    <xsl:apply-templates select="key('kHeaders1', generate-id())" /> 
    </Topic> 
</xsl:template> 

<xsl:template match="html"> 
    <Root> 
    <xsl:apply-templates select="body/h1" /> 
    </Root> 
</xsl:template> 

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

Cela fonctionne assez bien pour le premier niveau, mais en essayant de répéter le processus, mais en utilisant h2, semble briser mon esprit. Puisque, au niveau h2, la clé d'un nœud devrait être la première, h1 ou h2. On dirait presque qu'il pourrait être combiné en un seul ensemble de clés, où l'identifiant est quel que soit le dernier h * qui l'a précédé, et où les éléments h * ne sont pas listés dans le groupement (de sorte qu'ils ne sont pas récurrents). J'imagine quelque chose comme:

<xsl:key name="kHeaders" match="*[not(self::h1 or self::h2)]" use="generate-id(preceding-sibling::*[self::h1 or self::h2][1])"/> 

Cependant, qui laisse les éléments h2 de la liste, qui doivent être présents dans le groupe pour la précédente h1. Et si je relâche les restrictions sur le match pour inclure les éléments h1/h2 (et que les templates h1 correspondent aussi à h2), alors je récupèrerai les h2 de la h1 et ainsi de suite (un peu attendu).

Une solution idéale est celle qui peut être étendue pour travailler pour h3, h4, et ainsi de suite sans trop d'efforts. Cependant, il n'a pas besoin d'inclure des éléments de script pour gérer les éléments h * génériques. Des instructions simples pour ajouter un calque supplémentaire seraient suffisantes.

Quelqu'un a-t-il un conseil?

Répondre

1

Ci-dessous stylesheet (la plupart du code essentiel copié à partir this answer) travailleraient quand plusieurs en-têtes sont impliqués:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:key name="next-headings" match="h6" 
      use="generate-id(preceding-sibling::*[self::h1 or self::h2 or 
               self::h3 or self::h4 or 
               self::h5][1])" /> 

    <xsl:key name="next-headings" match="h5" 
      use="generate-id(preceding-sibling::*[self::h1 or self::h2 or 
               self::h3 or self::h4][1])" /> 
    <xsl:key name="next-headings" match="h4" 
      use="generate-id(preceding-sibling::*[self::h1 or self::h2 or 
               self::h3][1])" /> 
    <xsl:key name="next-headings" match="h3" 
      use="generate-id(preceding-sibling::*[self::h1 or self::h2][1])" /> 

    <xsl:key name="next-headings" match="h2" 
      use="generate-id(preceding-sibling::h1[1])" /> 

    <xsl:key name="immediate-nodes" 
      match="node()[not(self::h1 | self::h2 | self::h3 | self::h4 | 
          self::h5 | self::h6)]" 
      use="generate-id(preceding-sibling::*[self::h1 or self::h2 or 
               self::h3 or self::h4 or 
               self::h5 or self::h6][1])" /> 

    <xsl:template match="/"> 
     <Root> 
      <xsl:apply-templates select="html/body/h1"/> 
     </Root> 
    </xsl:template> 

    <xsl:template match="p"> 
     <Paragraph> 
      <xsl:value-of select="."/> 
     </Paragraph> 
    </xsl:template> 

    <xsl:template match="h1 | h2 | h3 | h4 | h5 | h6"> 
     <Topic> 
      <Title> 
       <xsl:value-of select="."/> 
      </Title> 
      <xsl:apply-templates select="key('immediate-nodes', generate-id())"/> 
      <xsl:apply-templates select="key('next-headings', generate-id())"/> 
     </Topic> 
    </xsl:template> 

</xsl:stylesheet> 
+0

Parfait! Je ne l'avais pas trouvé quand je cherchais les postes, et il semble qu'il avait la majorité de ce dont j'avais besoin. Merci de l'avoir adapté et de sauver ma santé mentale. Je n'avais aucune idée que je pouvais "ajouter" aux touches. Je les ai seulement définis une fois. J'ai donc aussi appris quelque chose! – Greg

+0

De rien! :) –

1

Cela fera l'affaire:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 

    <xsl:template match="/"> 
    <Root> 
     <xsl:apply-templates select="//h1"/> 
    </Root> 
    </xsl:template> 

    <xsl:template match="*[starts-with(local-name(), 'h')]"> 
    <xsl:variable name="lvl" select="number(substring-after(local-name(), 'h'))"/> 
    <Topic> 
     <Title> 
     <xsl:value-of select="text()"/> 
     </Title> 
     <xsl:apply-templates select="//following-sibling::*[not(starts-with(local-name(), 'h')) 
          and preceding-sibling::*[starts-with(local-name(), 'h')][1] = current()]"/> 
     <xsl:apply-templates select="//following-sibling::*[local-name() = concat('h', $lvl + 1) 
          and preceding-sibling::*[local-name() = concat('h', $lvl)][1] = current()]"/> 
    </Topic> 
    </xsl:template> 

    <xsl:template match="*"> 
    <Paragraph> 
     <xsl:value-of select="text()"/> 
    </Paragraph> 
    </xsl:template> 
</xsl:stylesheet> 
+0

J'ai testé aussi celui-ci et semble à l'astuce, mais sans clés. C'est aussi un peu plus condensé. Pas tout à fait aussi sage de savoir que cela pourrait être fait simplement, mais toujours une réponse parfaitement valide! – Greg

+0

@Greg, merci grâce –