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

StackOverflow https://stackoverflow.com/questions/184431

  •  06-07-2019
  •  | 
  •  

Pregunta

Estoy tratando de convertir un archivo XML en el formato utilizado por dokuwiki, el uso de XSLT.Esto realmente funciona hasta cierto punto, pero la sangría en el archivo XSL es llegar inserta en los resultados.Por el momento, tengo dos opciones:abandonar este XSLT cosa completamente distinta, y encontrar otra manera de convertir XML a dokuwiki marcado, o eliminar el 95% de los espacios en blanco del archivo XSL, lo que es casi-ilegible y una pesadilla de mantenimiento.

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

Antecedentes:Estoy migrando un autodoc herramienta de páginas HTML estáticas a dokuwiki, por lo que la API desarrollada por el equipo del servidor puede ser documentado por las aplicaciones del equipo cuando el equipo de aplicaciones se ejecuta en mal documentado el código.La lógica es tener una sección de cada página aparte para el autodoc herramienta, y para permitir los comentarios en cualquier lugar fuera de este bloque.Estoy usando XSLT, porque ya tenemos el archivo XSL para convertir XML, XHTML, y supongo que será más rápido para volver a escribir el archivo XSL que rollo mi propia solución.

Editar:Ah, a la derecha, tonto de mí, me olvidé de la sangría de atributo.(Otra nota de antecedentes:Soy nuevo en XSLT.) Por otro lado, todavía tengo que lidiar con el carácter de nueva línea.Dokuwiki utiliza tubos de diferenciar entre las columnas de la tabla, lo que significa que todos los datos en una tabla de la línea debe estar en una línea.Es allí una manera de suprimir el carácter de nueva línea se obtienen (sólo de vez en cuando), así que puedo hacer un poco bastante lógica compleja para cada celda de la tabla en un legibles fasion?

¿Fue útil?

Solución

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

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

Voy a hablar de los tres porque puede ser difícil saber donde los espacios en blanco proviene de lo que puede necesitar el uso de varias estrategias.

Para abordar el espacio en blanco que se encuentra entre los nodos en el documento de origen, usted debe utilizar <xsl:strip-space> para eliminar cualquier espacio en blanco que aparece entre dos nodos y, a continuación, utilizar <xsl:preserve-space> para preservar el importante espacio en blanco que puede aparecer dentro de contenido mixto.Por ejemplo, si el documento de origen se ve así:

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

a continuación, se desea ignorar los espacios en blanco entre el <ul> y el <li> y entre el </li> y el </ul>, lo que no es significativo, pero conservar el espacio en blanco entre el <strong> y <em> elementos, que es significativo (de lo contrario se obtendría "Esto es una **importante****punto").Para hacer esto se utiliza

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

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

A un lado:el uso de <xsl:strip-space> también reduce el tamaño de la fuente del árbol de la memoria, y hace que su hoja de estilos más eficiente, por lo que vale la pena hacerlo, incluso si usted no tiene espacios en blanco en los problemas de este tipo.

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

<dt>
  a definition
</dt>

y usted puede estar seguro de que el <dt> elemento no se puede sostener cualquier elemento que desea hacer algo con, entonces usted puede hacer:

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

El principio y el final en los espacios en blanco serán despojados de el valor de la <dt> elemento y usted acaba de obtener la cadena "a definition".

Para abordar el espacio en blanco que viene de la hoja de estilos, que es quizás el que estás viviendo, es cuando se tiene un texto dentro de una plantilla como esta:

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

Hojas de estilos XSLT que se analizan de la misma manera que los documentos de origen que el proceso, por lo que la anterior XSLT se interpreta como un árbol que tiene un <xsl:template> elemento con un match atributo cuyo primer hijo es un nodo de texto y cuyo segundo hijo es un <xsl:value-of> elemento con un select atributo.El texto tiene el nodo inicial y final en blanco (incluyendo los saltos de línea);ya que el texto literal en la hoja de estilos, se pone literalmente copiado en el resultado, con todos los iniciales y finales en los espacios en blanco.

Pero algunos los espacios en blanco en las hojas de estilos XSLT se despojaron de forma automática, es decir, aquellos entre los nodos.Usted no consigue un salto de línea en el resultado porque hay un salto de línea entre el <xsl:value-of> y el cierre de la <xsl:template>.

Para obtener sólo el texto que desee en el resultado, el uso de la <xsl:text> elemento como este:

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

El procesador XSLT se ignoran los saltos de línea y sangría que aparecen entre los nodos, y sólo la salida del texto dentro de la <xsl:text> elemento.

Otros consejos

¿Está usando sangría = " no " en su etiqueta de salida?

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

También si está usando xsl: value-of puede usar el deshabilitado-output-escape = " yes " para ayudar con algunos problemas de espacios en blanco.

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

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

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

Salidas:

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 un nodo de texto que contiene al menos un carácter que no es un 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 reglas para eliminar espacios en blanco dicen que los nodos de texto de espacio en blanco solo se eliminan, la nueva línea y la sangría entre < xsl: template > y < xsl: text > desnudarse (bien). Dado que las reglas dicen que se conserva un nodo de texto con al menos un carácter de espacio en blanco, 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 de strip / preserve / normalize). El & Quot; & Amp; n; & Quot; al final de la línea inserta una nueva línea, pero también asegura que se ignore cualquier espacio en blanco siguiente, porque aparece entre dos nodos.

El problema que tengo es cuando quiero generar una línea con sangría que comienza con < xsl: value-of > ;. En ese caso, el & Quot; & Amp; e; & Quot; no ayudará, porque el espacio en blanco de sangría no es " adjunto " a cualquier carácter que no sea un espacio en blanco. Entonces, para esos casos, uso & Quot; & Amp; s2; & Quot; o " & amp; s4; " ;, dependiendo de la sangría que quiera.

Es un truco feo, estoy seguro, pero al menos no tengo el verbose " < xsl: text > " etiquetas que ensucian mi XSLT, y al menos todavía puedo sangrar el XSLT en sí 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 & Quot; macros & Quot ;:

<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 prevista, y arruina la sangría del propio XSL porque las etiquetas finales </xsl:text> tienen que aparecer en la columna 1 del archivo XSL (de lo contrario, obtendrá un espacio en blanco no deseado en el archivo de salida).

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ámelo 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>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top