xsl transformar online generate generador from create convertir xml xslt whitespace dokuwiki

transformar - Conversión de XML a texto sin formato: ¿cómo debo ignorar/manejar espacios en blanco en el XSLT?



xslt editor (4)

¿Estás usando indent = "no" en tu etiqueta de salida?

<xsl:output method="text" indent="no" />

Además, si usa xsl: value-of, puede usar disable-output-escaping = "yes" para ayudar con algunos problemas de espacio en blanco.

Estoy tratando de convertir un archivo XML en el marcado utilizado por dokuwiki, usando XSLT. En realidad, esto funciona hasta cierto punto, pero la sangría en el archivo XSL se está insertando en los resultados. Por el momento, tengo dos opciones: abandonar esta cosa XSLT por completo, y buscar otra forma de convertir de XML a marcado dokuwiki, o eliminar aproximadamente el 95% de los espacios en blanco del archivo XSL, convirtiéndolo casi ilegible y una pesadilla de mantenimiento.

¿Hay alguna forma de mantener la sangría en el archivo XSL sin pasar todo ese espacio en blanco en el documento final?

Antecedentes: estoy migrando una herramienta autodoc de páginas HTML estáticas a dokuwiki, por lo que la API desarrollada por el equipo servidor puede ser documentada por el equipo de aplicaciones siempre que el equipo de aplicaciones se encuentre con un código mal documentado. La lógica es tener una sección de cada página reservada para la herramienta autodoc y permitir comentarios en cualquier lugar fuera de este bloque. Estoy usando XSLT porque ya tenemos el archivo XSL para convertir de XML a XHTML, y asumo que será más rápido reescribir el XSL que rodar mi propia solución desde cero.

Editar: Ah, claro, me engañas, descuidé el atributo de sangría. (Otra nota de fondo: soy nuevo en XSLT.) Por otro lado, todavía tengo que ocuparme de las nuevas líneas. Dokuwiki utiliza tuberías para diferenciar entre columnas de la tabla, lo que significa que todos los datos en una línea de la tabla deben estar en una línea. ¿Hay alguna manera de suprimir las nuevas líneas de salida (solo ocasionalmente), entonces puedo hacer una lógica bastante compleja para cada celda de la tabla en un fasion algo legible?


Con respecto a su edición sobre nuevas líneas, puede usar esta plantilla para reemplazar recursivamente una cadena dentro de otra cadena, y puede usarla para saltos de línea:

<xsl:template name="replace.string.section"> <xsl:param name="in.string"/> <xsl:param name="in.characters"/> <xsl:param name="out.characters"/> <xsl:choose> <xsl:when test="contains($in.string,$in.characters)"> <xsl:value-of select="concat(substring-before($in.string,$in.characters),$out.characters)"/> <xsl:call-template name="replace.string.section"> <xsl:with-param name="in.string" select="substring-after($in.string,$in.characters)"/> <xsl:with-param name="in.characters" select="$in.characters"/> <xsl:with-param name="out.characters" select="$out.characters"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$in.string"/> </xsl:otherwise> </xsl:choose> </xsl:template>

Llámalo de la siguiente manera (este ejemplo reemplaza los saltos de línea en la variable $ some.string con un espacio):

<xsl:call-template name="replace.string.section"> <xsl:with-param name="in.string" select="$some.string"/> <xsl:with-param name="in.characters" select="''&#xA;''"/> <xsl:with-param name="out.characters" select="'' ''"/> </xsl:call-template>


Hay tres razones para obtener espacios en blanco no deseados en el resultado de una transformación XSLT:

  1. espacio en blanco que proviene de entre nodos en el documento de origen
  2. espacio en blanco que proviene de los nodos en el documento de origen
  3. espacio en blanco que proviene de la hoja de estilo

Voy a hablar de los tres porque puede ser difícil saber de dónde viene el espacio en blanco, por lo que es posible que deba utilizar varias estrategias.

Para abordar el espacio en blanco que se encuentra entre los nodos en su documento fuente, debe usar <xsl:strip-space> para eliminar cualquier espacio en blanco que aparezca entre dos nodos, y luego use <xsl:preserve-space> para conservar el espacio en blanco significativo que podría aparecer dentro de contenido mixto. Por ejemplo, si su documento fuente se ve así:

<ul> <li>This is an <strong>important</strong> <em>point</em></li> </ul>

entonces querrá ignorar los espacios en blanco entre el <ul> y el <li> y entre el </li> y el </ul> , que no es significativo, pero conserva los espacios en blanco entre <strong> y <em> elementos, que es significativo (de lo contrario, obtendría "Este es un ** punto *** importante *"). Para hacer esto uso

<xsl:strip-space elements="*" /> <xsl:preserve-space elements="li" />

El atributo de elements en <xsl:preserve-space> básicamente debe enumerar todos los elementos en su documento que tienen contenido mixto.

Aparte: usar <xsl:strip-space> también reduce el tamaño del árbol de fuentes en la memoria y hace que su hoja de estilos sea más eficiente, por lo que vale la pena incluso si no tiene problemas de espacio en blanco de este tipo.

Para abordar el espacio en blanco que aparece dentro de los nodos en su documento de origen, debe usar normalize-space() . Por ejemplo, si tiene:

<dt> a definition </dt>

y puede estar seguro de que el elemento <dt> no contendrá ningún elemento con el que quiera hacer algo, entonces puede hacer:

<xsl:template match="dt"> ... <xsl:value-of select="normalize-space(.)" /> ... </xsl:template>

El espacio en blanco inicial y final se eliminará del valor del elemento <dt> y obtendrá la cadena "a definition" .

Para abordar el espacio en blanco proveniente de la hoja de estilos, que tal vez sea la que estás experimentando, es cuando tienes texto dentro de una plantilla como esta:

<xsl:template match="name"> Name: <xsl:value-of select="." /> </xsl:template>

Las hojas de estilos XSLT se analizan de la misma manera que los documentos fuente que procesan, por lo que el XSLT anterior se interpreta como un árbol que contiene un elemento <xsl:template> con un atributo de match cuyo primer hijo es un nodo de texto y cuyo segundo hijo es un elemento <xsl:value-of> con un atributo de select . El nodo de texto tiene espacios en blanco iniciales y finales (incluidos saltos de línea); dado que es texto literal en la hoja de estilo, se copia literalmente en el resultado, con todos los espacios en blanco iniciales y finales.

Pero algunos espacios en blanco en las hojas de estilos XSLT se eliminan automáticamente, es decir, aquellos entre los nodos. No obtiene un salto de línea en su resultado porque hay un salto de línea entre <xsl:value-of> y el cierre de <xsl:template> .

Para obtener solo el texto que desea en el resultado, use el elemento <xsl:text> como este:

<xsl:template match="name"> <xsl:text>Name: </xsl:text> <xsl:value-of select="." /> </xsl:template>

El procesador XSLT ignorará los saltos de línea y la sangría que aparecen entre los nodos, y solo generará el texto dentro del elemento <xsl:text> .


La respuesta de @JeniT es genial, solo quiero señalar un truco para administrar el espacio en blanco. No estoy seguro de que sea la mejor manera (o incluso una buena forma), pero a mí me funciona por ahora.

("s" para espacio, "e" para vacío, "n" para línea nueva).

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xsl:transform [ <!ENTITY s "<xsl:text xmlns:xsl=''http://www.w3.org/1999/XSL/Transform''> </xsl:text>" > <!ENTITY s2 "<xsl:text xmlns:xsl=''http://www.w3.org/1999/XSL/Transform''> </xsl:text>" > <!ENTITY s4 "<xsl:text xmlns:xsl=''http://www.w3.org/1999/XSL/Transform''> </xsl:text>" > <!ENTITY s6 "<xsl:text xmlns:xsl=''http://www.w3.org/1999/XSL/Transform''> </xsl:text>" > <!ENTITY e "<xsl:text xmlns:xsl=''http://www.w3.org/1999/XSL/Transform''></xsl:text>" > <!ENTITY n "<xsl:text xmlns:xsl=''http://www.w3.org/1999/XSL/Transform''> </xsl:text>" > ]> <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:output method="text"/> <xsl:template match="/"> &e;Flush left, despite the indentation.&n; &e; This line will be output indented two spaces.&n; <!-- the blank lines above/below won''t be output --> <xsl:for-each select="//foo"> &e; Starts with two blanks: <xsl:value-of select="@bar"/>.&n; &e; <xsl:value-of select="@baz"/> The ''e'' trick won''t work here.&n; &s2;<xsl:value-of select="@baz"/> Use s2 instead.&n; &s2; <xsl:value-of select="@abc"/> <xsl:value-of select="@xyz"/>&n; &s2; <xsl:value-of select="@abc"/>&s;<xsl:value-of select="@xyz"/>&n; </xsl:for-each> </xsl:template> </xsl:transform>

Aplicado a:

<?xml version="1.0" encoding="UTF-8"?> <foo bar="bar" baz="baz" abc="abc" xyz="xyz"></foo>

Productos:

Flush left, despite the indentation. This line will be output indented two spaces. Starts with two blanks: bar. baz The ''e'' trick won''t work here. baz Use s2 instead. abcxyz abc xyz

El truco "e" funciona antes de que un nodo de texto contenga al menos un carácter que no sea de espacio en blanco porque se expande a esto:

<xsl:template match="/"> <xsl:text></xsl:text>Flush left, despite the indentation.<xsl:text> </xsl:text>

Dado que las w3.org/TR/xslt#strip indican que los nodos de texto solo en w3.org/TR/xslt#strip , la nueva línea y la sangría entre la <xsl: plantilla> y <xsl: texto> se eliminan (bien). Dado que las reglas dicen que un nodo de texto con al menos un carácter de espacio en blanco se conserva, el nodo de texto implícito que contiene " This line will be output indented two spaces." mantiene su espacio en blanco inicial (pero supongo que esto también depende de la configuración para quitar / conservar / normalizar). Entonces;" al final de la línea inserta una nueva línea, pero también asegura que se ignore cualquier espacio en blanco siguiente, ya que aparece entre dos nodos.

El problema que tengo es cuando quiero dar salida a una línea sangrada que comienza con un <xsl: value-of>. En ese caso, el "& e;" no ayudará, porque el espacio en blanco de la indentación no está "conectado" a ningún carácter que no sea de espacios en blanco. Entonces, para esos casos, uso "& s2;" o "& s4;", según la cantidad de sangría que desee.

Es un hack feo, estoy seguro, pero al menos no tengo las etiquetas "<xsl: text>" que ensucian mi XSLT, y al menos puedo sangrar el XSLT mismo para que sea legible. Siento que estoy abusando de XSLT por algo para lo que no fue diseñado (procesamiento de texto) y esto es lo mejor que puedo hacer.

Editar: En respuesta a los comentarios, esto es lo que parece sin las "macros":

<xsl:template match="/"> <xsl:text>Flush left, despite the indentation.</xsl:text> <xsl:text> This line will be output indented two spaces.</xsl:text> <xsl:for-each select="//foo"> <xsl:text> Starts with two blanks: </xsl:text><xsl:value-of select="@bar"/>.<xsl:text> </xsl:text> <xsl:text> </xsl:text><xsl:value-of select="@abc"/><xsl:text> </xsl:text><xsl:value-of select="@xyz"/><xsl:text> </xsl:text> </xsl:for-each> </xsl:template>

Creo que eso hace que sea menos claro ver la sangría de salida deseada, y arruina la sangría del XSL en sí porque las etiquetas de fin </xsl:text> tienen que aparecer en la columna 1 del archivo XSL (de lo contrario, obtiene espacios en blanco no deseados) en el archivo de salida).