2010-08-23 12 views
9

estoy transformación XSLT mis-hojas de estilo en la documentación, y quiero una experiencia rica dentro de los nodos de comentario para cada código-trozo, por lo tanto, quiero convertir el siguiente comentario y la salida como XHTML:la conversión de rebajas sencilla (cadena) a HTML con XSLT

cadena:

 
# This is a title with __bold__ text and *italic* # 
This is just a normal line 

- list point with some __bold__ 
- list point with a "link"[http://www.stackoverflow.com] 

salida deseada:

<h1> This is a title with <strong>bold</strong> and <span>italic</span> </h1> 
<p>This is a normal line</p> 

<ul> 
    <li>list point with some <strong>bold</strong></li> 
    <li>list point with a <a href="http://www.stackoverflow.com">link</a></li> 
</ul> 

me trataron con una función recursiva que utiliza XSL: analizar cuerdas de forma recursiva a partir de una regla establecer, pero no puede encontrar una solución que funciona realmente bien.

Cualquiera haber hecho esto últimamente, o hay algunos marcos por ahí que tiene funciones para hacer esto?

Gracias de antemano! :)

Editar: Añadido un ejemplo sucia:

<!-- Output comments --> 
<xsl:template match="comment()" mode="COMMENT"> 
    <xsl:copy-of select="ips:groupReplace(normalize-space(.), 
     ' 
     (.*)(\n|\r)(.*), 
     (.*)\*(.*)\*(.*), 
     (.*)\*\*(.*)\*\*(.*), 
     (.*)__(.*)__(.*), 
     (.*)#(.*)#(.*), 
     (.*)-(.*) 
     ', 
     ' 
     br, 
     span.italic, 
     span.bold, 
     strong, 
     h1, 
     li 
     ')" /> 
</xsl:template> 

<!-- Initializing the iterateRegex function --> 
<xsl:function name="ips:groupReplace"> 
    <xsl:param name="string" as="xs:string" /> 
    <xsl:param name="search" /> 
    <xsl:param name="replace" /> 
    <xsl:variable name="regex" select="tokenize($search, ',')" /> 
    <xsl:variable name="replacements" select="tokenize($replace, ',')" /> 
    <xsl:copy-of select="ips:iterateRegex(count($replacements), $string, $regex, $replacements)" /> 
</xsl:function> 

<!-- Iterate each regex --> 
<xsl:function name="ips:iterateRegex"> 
    <xsl:param name="counter" /> 
    <xsl:param name="string" /> 
    <xsl:param name="list_regex" /> 
    <xsl:param name="list_replace" /> 
    <xsl:variable name="newStr"> 
    <xsl:analyze-string select="$string" regex="{normalize-space($list_regex[$counter])}" flags="xm"> 
     <xsl:matching-substring> 
      <xsl:variable name="cc" select="contains($list_replace[$counter], '.')" /> 
      <xsl:variable name="tag" select="normalize-space(if ($cc) then (substring-before($list_replace[$counter], '.')) else ($list_replace[$counter]))" /> 
      <xsl:copy-of select="regex-group(1)" /> 
      <xsl:choose> 
       <xsl:when test="normalize-space(regex-group(2)) = ''"> 
       <xsl:element name="{$tag}" /> 
       </xsl:when> 
       <xsl:otherwise> 
       <xsl:element name="{$tag}" > 
        <xsl:if test="$cc"> 
        <xsl:attribute name="class" select="substring-after($list_replace[$counter],'.')" /> 
        </xsl:if> 
        <xsl:copy-of select="regex-group(2)" /> 
       </xsl:element> 
       </xsl:otherwise> 
      </xsl:choose> 
      <xsl:copy-of select="regex-group(3)" /> 
     </xsl:matching-substring> 
     <xsl:non-matching-substring> 
     <xsl:copy-of select="." /> 
     </xsl:non-matching-substring> 
    </xsl:analyze-string> 
    </xsl:variable> 
    <xsl:variable name="count" select="number($counter) - 1" /> 
    <xsl:choose> 
    <xsl:when test="$count &gt; 0"> 
     <xsl:copy-of select="ips:iterateRegex($count, $newStr, $list_regex, $list_replace)" />  
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:copy-of select="$newStr" /> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:function> 
+2

¿Por qué tratarías de transformar contenido no XML usando XSLT? Es una herramienta completamente incorrecta para el trabajo. –

+1

@Greg: ese tipo de depende de la versión de XSLT que puede usar. Con XSLT 1.0, de hecho, muy difícil. Pero con XSLT 2.0 y su extenso regex y otro soporte de procesamiento de cadenas, y funciones arbitrarias de carga de documentos de texto, se vuelve casi trivial. – Abel

+0

@Sveisvei: ¿qué versión o procesador de XSLT usas? ¿Puedes usar métodos o funciones de extensión y, de ser así, en qué idioma? – Abel

Respuesta

9

Esta transformación (111 líneas):

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
xmlns:my="my:my" 
exclude-result-prefixes="xml xsl xs my"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/"> 
    <xsl:variable name="vLines" select="tokenize(., '\n')"/> 

    <xsl:sequence select="my:parse-lines($vLines)"/> 
</xsl:template> 

<xsl:function name="my:parse-lines" as="element()*"> 
    <xsl:param name="pLines" as="xs:string*"/> 

    <xsl:sequence select= 
     "my:parse-line($pLines, 1, count($pLines))"/> 
</xsl:function> 

<xsl:function name="my:parse-line" as="element()*"> 
    <xsl:param name="pLines" as="xs:string*"/> 
    <xsl:param name="pLineNum" as="xs:integer"/> 
    <xsl:param name="pTotalLines" as="xs:integer"/> 

    <xsl:if test="not($pLineNum gt $pTotalLines)"> 
    <xsl:variable name="vLine" select="$pLines[$pLineNum]"/> 
    <xsl:variable name="vLineLength" 
     select="string-length($vLine)"/> 
     <xsl:choose> 
     <xsl:when test= 
     "starts-with($vLine, '#') 
     and 
     ends-with($vLine, '#') 
     "> 
     <xsl:variable name="vInnerString" 
     select="substring($vLine, 2, $vLineLength -2)"/> 
     <h1> 
     <xsl:sequence select="my:parse-string($vInnerString)"/> 
     </h1> 
     <xsl:sequence select= 
     "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/> 
     </xsl:when> 
     <xsl:when test= 
     "starts-with($vLine, '- ') 
     and 
     not(starts-with($pLines[$pLineNum -1], '- ')) 
     "> 
     <ul> 
      <li> 
      <xsl:sequence select="my:parse-string(substring($vLine, 2))"/> 
      </li> 
      <xsl:sequence select= 
      "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/> 
     </ul> 
     </xsl:when> 
     <xsl:when test="starts-with($vLine, '- ')"> 
      <li> 
      <xsl:sequence select="my:parse-string(substring($vLine, 2))"/> 
      </li> 
      <xsl:sequence select= 
      "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/> 
     </xsl:when> 
     <xsl:otherwise> 
     <p> 
      <xsl:sequence select="my:parse-string($vLine)"/> 
     </p> 
     <xsl:sequence select= 
      "my:parse-line($pLines, $pLineNum+1, $pTotalLines)"/> 
     </xsl:otherwise> 
     </xsl:choose> 
    </xsl:if> 
</xsl:function> 

<xsl:function name="my:parse-string" as="node()*"> 
    <xsl:param name="pS" as="xs:string"/> 

    <xsl:analyze-string select="$pS" flags="x" regex= 
    '(__(.*?)__) 
    | 
    (\*(.*?)\*) 
    | 
    ("(.*?)"\[(.*?)\]) 

    '> 
    <xsl:matching-substring> 
    <xsl:choose> 
    <xsl:when test="regex-group(1)"> 
     <strong> 
      <xsl:sequence select="my:parse-string(regex-group(2))"/> 
     </strong> 
    </xsl:when> 
    <xsl:when test="regex-group(3)"> 
     <span> 
      <xsl:sequence select="my:parse-string(regex-group(4))"/> 
     </span> 
    </xsl:when> 
    <xsl:when test="regex-group(5)"> 
     <a href="{regex-group(7)}"> 
     <xsl:sequence select="regex-group(6)"/> 
     </a> 
    </xsl:when> 
    </xsl:choose> 
    </xsl:matching-substring> 

    <xsl:non-matching-substring> 
    <xsl:value-of select="."/> 
    </xsl:non-matching-substring> 
    </xsl:analyze-string> 
</xsl:function> 
</xsl:stylesheet> 

cuando se aplica en este documento XML (el texto proporcionado complicada con anidada construcciones y envuelto en un elemento):

<t># This is a title with __bold__ text and *italic* # 
This is just a normal line 

- list point with some __bold__ 
- list point with a __*"link"[http://www.stackoverflow.com]*__</t> 

produce el deseado, salida correcta:

<h1> This is a title with <strong>bold</strong> text and <span>italic</span> 
</h1> 
<p>This is just a normal line</p> 
<p/> 
<ul> 
    <li> list point with some <strong>bold</strong> 
    </li> 
    <li> list point with a <strong> 
     <span> 
      <a href="http://www.stackoverflow.com">link</a> 
     </span> 
     </strong> 
    </li> 
</ul> 

hacer la nota: El mecanismo de expresiones regulares de XPath 2.0 y XSLT 2.0 es adecuada para resolver este problema.

+0

+1 ¡Agradable! ¡Sabía que no debería ser demasiado difícil crear una plantilla básica! – Abel

+0

Gracias otra vez Dimitre. Ill opensource la generación de documentación en github cuando está hecho, por lo que es usuable para otras personas :) – Sveisvei

+0

@Dimitre: +1 El uso de 'function' en lugar de' template', ciertamente hace que la hoja de estilos sea más compacta. Pero, creo que su 'parse-line' y' parse-string' (así como mi propio 'block' y' inline') funcionan como producciones gramaticales (por lo tanto, de análisis) que es el concepto básico que falta en el intento de OP. –

13

Creo que se necesita un analizador. Así que esta hoja de estilo implementa un detallado uno:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="text" name="block"> 
     <xsl:param name="pString" select="."/> 
     <xsl:if test="$pString != ''"> 
      <xsl:choose> 
       <xsl:when test="starts-with($pString,'#')"> 
        <xsl:call-template name="header"> 
         <xsl:with-param name="pString" 
         select="substring($pString,2)"/> 
        </xsl:call-template> 
       </xsl:when> 
       <xsl:when test="starts-with($pString,'&#xA;')"> 
        <xsl:call-template name="list"> 
         <xsl:with-param name="pString" 
         select="substring($pString,2)"/> 
        </xsl:call-template> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:call-template name="paragraph"> 
         <xsl:with-param name="pString" 
               select="$pString"/> 
        </xsl:call-template> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:if> 
    </xsl:template> 
    <xsl:template name="header"> 
     <xsl:param name="pString"/> 
     <xsl:variable name="vInside" 
     select="substring-before($pString,'#&#xA;')"/> 
     <xsl:choose> 
      <xsl:when test="$vInside != ''"> 
       <h1> 
        <xsl:call-template name="inline"> 
         <xsl:with-param name="pString" select="$vInside"/> 
        </xsl:call-template> 
       </h1> 
       <xsl:call-template name="block"> 
        <xsl:with-param name="pString" 
        select="substring-after($pString,'#&#xA;')"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:call-template name="paragraph"> 
        <xsl:with-param name="pString" 
            select="concat('#',$pString)"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template name="list"> 
     <xsl:param name="pString"/> 
     <xsl:variable name="vCheckList" select="starts-with($pString,'- ')"/> 
     <xsl:choose> 
      <xsl:when test="$vCheckList"> 
       <ul> 
        <xsl:call-template name="listItem"> 
         <xsl:with-param name="pString" select="$pString"/> 
        </xsl:call-template> 
       </ul> 
       <xsl:call-template name="block"> 
        <xsl:with-param name="pString"> 
         <xsl:call-template name="afterlist"> 
          <xsl:with-param name="pString" select="$pString"/> 
         </xsl:call-template> 
        </xsl:with-param> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:call-template name="block"> 
        <xsl:with-param name="pString" select="$pString"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template name="paragraph"> 
     <xsl:param name="pString"/> 
     <xsl:choose> 
      <xsl:when test="contains($pString,'&#xA;')"> 
       <p> 
        <xsl:value-of select="substring-before($pString,'&#xA;')"/> 
       </p> 
      </xsl:when> 
      <xsl:otherwise> 
       <p> 
        <xsl:value-of select="$pString"/> 
       </p> 
      </xsl:otherwise> 
     </xsl:choose> 
     <xsl:call-template name="block"> 
      <xsl:with-param name="pString" 
      select="substring-after($pString,'&#xA;')"/> 
     </xsl:call-template> 
    </xsl:template> 
    <xsl:template name="afterlist"> 
     <xsl:param name="pString"/> 
     <xsl:choose> 
      <xsl:when test="starts-with($pString,'- ')"> 
       <xsl:call-template name="afterlist"> 
        <xsl:with-param name="pString" 
        select="substring-after($pString,'&#xA;')"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="$pString"/> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template name="listItem"> 
     <xsl:param name="pString"/> 
     <xsl:if test="starts-with($pString,'- ')"> 
      <li> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" 
        select="substring-before(substring($pString,3),'&#xA;')"/> 
       </xsl:call-template> 
      </li> 
      <xsl:call-template name="listItem"> 
       <xsl:with-param name="pString" 
       select="substring-after($pString,'&#xA;')"/> 
      </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 
    <xsl:template name="inline"> 
     <xsl:param name="pString" select="."/> 
     <xsl:if test="$pString != ''"> 
      <xsl:choose> 
       <xsl:when test="starts-with($pString,'__')"> 
        <xsl:call-template name="strong"> 
         <xsl:with-param name="pString" 
         select="substring($pString,3)"/> 
        </xsl:call-template> 
       </xsl:when> 
       <xsl:when test="starts-with($pString,'*')"> 
        <xsl:call-template name="span"> 
         <xsl:with-param name="pString" 
         select="substring($pString,2)"/> 
        </xsl:call-template> 
       </xsl:when> 
       <xsl:when test="starts-with($pString,'&quot;')"> 
        <xsl:call-template name="link"> 
         <xsl:with-param name="pString" 
         select="substring($pString,2)"/> 
        </xsl:call-template> 
       </xsl:when> 
       <xsl:otherwise> 
        <xsl:value-of select="substring($pString,1,1)"/> 
        <xsl:call-template name="inline"> 
         <xsl:with-param name="pString" 
         select="substring($pString,2)"/> 
        </xsl:call-template> 
       </xsl:otherwise> 
      </xsl:choose> 
     </xsl:if> 
    </xsl:template> 
    <xsl:template name="strong"> 
     <xsl:param name="pString"/> 
     <xsl:variable name="vInside" select="substring-before($pString,'__')"/> 
     <xsl:choose> 
      <xsl:when test="$vInside != ''"> 
       <strong> 
        <xsl:value-of select="$vInside"/> 
       </strong> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" 
        select="substring-after($pString,'__')"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="'__'"/> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" select="$pString"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template name="span"> 
     <xsl:param name="pString"/> 
     <xsl:variable name="vInside" select="substring-before($pString,'*')"/> 
     <xsl:choose> 
      <xsl:when test="$vInside != ''"> 
       <span> 
        <xsl:value-of select="$vInside"/> 
       </span> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" 
        select="substring-after($pString,'*')"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="'*'"/> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" select="$pString"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template name="link"> 
     <xsl:param name="pString"/> 
     <xsl:variable name="vInside" 
       select="substring-before($pString,'&quot;')"/> 
     <xsl:choose> 
      <xsl:when test="$vInside != ''"> 
       <xsl:call-template name="href"> 
        <xsl:with-param name="pString" 
        select="substring-after($pString,'&quot;')"/> 
        <xsl:with-param name="pInside" select="$vInside"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="'&quot;'"/> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" select="$pString"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template name="href"> 
     <xsl:param name="pString"/> 
     <xsl:param name="pInside"/> 
     <xsl:variable name="vHref" 
     select="substring-before(substring($pString,2),']')"/> 
     <xsl:choose> 
      <xsl:when test="starts-with($pString,'[') and $vHref != ''"> 
       <a href="{$vHref}"> 
        <xsl:value-of select="$pInside"/> 
       </a> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" 
        select="substring-after($pString,']')"/> 
       </xsl:call-template> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="concat('&quot;',$pInside,'&quot;')"/> 
       <xsl:call-template name="inline"> 
        <xsl:with-param name="pString" select="$pString"/> 
       </xsl:call-template> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

Con esta entrada:

<text> 
# This is a title with __bold__ text and *italic* # 
This is just a normal line 

- list point with some __bold__ 
- list point with a "link"[http://www.stackoverflow.com] 
</text> 

Salida:

<h1> This is a title with 
    <strong>bold</strong> text and 
    <span>italic</span> 
</h1> 
<p>This is just a normal line</p> 
<ul> 
    <li>list point with some 
     <strong>bold</strong> 
    </li> 
    <li>list point with a 
     <a href="http://www.stackoverflow.com">link</a> 
    </li> 
</ul> 

Nota: Mire cuántas plantillas son similares (que siguen un patrón), por lo que estos podrían ser parametrizados. No he hecho que en este caso, ya que parece que hay más preguntas que necesitan algún tipo de analizador, así que al final de la semana voy a publicar de nuevo una respuesta implementación de programa de análisis funcional y analizador combinadores patrón que hacen muy fácil de escribir analizadores (simplemente escribiendo sus reglas gramaticales).

Editar: solución XSLT 2.0. Esta hoja de estilo:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="text"> 
     <xsl:param name="pString" select="."/> 
     <xsl:analyze-string select="$pString" 
             regex="(#(.*)#&#xA;)|((- (.*)&#xA;)+)"> 
      <xsl:matching-substring> 
       <xsl:choose> 
        <xsl:when test="regex-group(1)"> 
         <h1> 
          <xsl:call-template name="inline"> 
           <xsl:with-param name="pString" 
             select="regex-group(2)"/> 
          </xsl:call-template> 
         </h1> 
        </xsl:when> 
        <xsl:when test="regex-group(3)"> 
         <ul> 
          <xsl:call-template name="list"> 
           <xsl:with-param name="pString" 
             select="regex-group(3)"/> 
          </xsl:call-template> 
         </ul> 
        </xsl:when> 
       </xsl:choose> 
      </xsl:matching-substring> 
      <xsl:non-matching-substring> 
       <xsl:if test=".!='&#xA;'"> 
        <p> 
         <xsl:call-template name="inline"> 
          <xsl:with-param name="pString" 
             select="normalize-space(.)"/> 
         </xsl:call-template> 
        </p> 
       </xsl:if> 
      </xsl:non-matching-substring> 
     </xsl:analyze-string> 
    </xsl:template> 
    <xsl:template name="list"> 
     <xsl:param name="pString"/> 
     <xsl:analyze-string select="$pString" regex="- (.*)&#xA;"> 
      <xsl:matching-substring> 
       <li> 
        <xsl:call-template name="inline"> 
         <xsl:with-param name="pString" 
            select="regex-group(1)"/> 
        </xsl:call-template> 
       </li> 
      </xsl:matching-substring> 
     </xsl:analyze-string> 
    </xsl:template> 
    <xsl:template name="inline"> 
     <xsl:param name="pString" select="."/> 
     <xsl:analyze-string select="$pString" 
       regex="(__(.*)__)|(\*(.*)\*)|(&quot;(.*)&quot;\[(.*)\])"> 
      <xsl:matching-substring> 
       <xsl:choose> 
        <xsl:when test="regex-group(1)"> 
         <strong> 
          <xsl:value-of select="regex-group(2)"/> 
         </strong> 
        </xsl:when> 
        <xsl:when test="regex-group(3)"> 
         <span> 
          <xsl:value-of select="regex-group(4)"/> 
         </span> 
        </xsl:when> 
        <xsl:when test="regex-group(5)"> 
         <a href="{regex-group(7)}"> 
          <xsl:value-of select="regex-group(6)"/> 
         </a> 
        </xsl:when> 
       </xsl:choose> 
      </xsl:matching-substring> 
      <xsl:non-matching-substring> 
       <xsl:value-of select="."/> 
      </xsl:non-matching-substring> 
     </xsl:analyze-string> 
    </xsl:template> 
</xsl:stylesheet> 

Salida:

<h1> This is a title with 
    <strong>bold</strong> text and 
    <span>italic</span> 
</h1> 
<p>This is just a normal line</p> 
<ul> 
    <li>list point with some 
     <strong>bold</strong> 
    </li> 
    <li>list point with a 
     <a href="http://www.stackoverflow.com">link</a> 
    </li> 
</ul> 
+2

+1 para un ejemplo complejo (¡y mucho trabajo!) en XSLT 1.0, (a pesar de que el asker solo necesita 2.0, lo que lo haría más fácil y mucho más pequeño ...;) – Abel

+0

@Abel: Solo algunas cosas podrían simplificarse con XSLT 2.0. RegExp no es el camino a seguir para resolver este problema en su conjunto, ya que necesita la funcionalidad del analizador. La gran mejora debería ser parametrizar todas las plantillas similares. Pero eso sería a mitad de camino para implementar el analizador funcional. –

+0

@Alejandro: posiblemente. Pero las expresiones regulares no fueron la única mejora en XSLT 2.0 ... pero a menos que se me ocurra una solución, no debería intentar comparar, realmente. – Abel