xml - example - xslt tutorial
XSLT: elimina el espacio en blanco de la plantilla (7)
Agrega una plantilla en tu xslt
<xsl:template match="text()"/>
Estoy usando XML para almacenar una pequeña lista de contactos e intentar escribir una plantilla XSL que la transformará en un archivo CSV. El problema que estoy teniendo es con espacio en blanco en la salida.
La salida:
Friend, John, Smith, Home,
123 test,
Sebastopol,
California,
12345,
Home 1-800-123-4567, Personal [email protected]
He sangrado / espaciado tanto el archivo XML de origen como la plantilla XSL asociada para que sea más fácil de leer y desarrollar, pero todo ese espacio en blanco adicional se está metiendo en el resultado. El XML en sí mismo no tiene espacios en blanco adicionales dentro de los nodos, justo afuera de ellos para el formateo, y lo mismo ocurre con el XSLT.
Para que el archivo CSV sea válido, cada entrada debe estar en su propia línea, no dividida. Además de eliminar todo el espacio en blanco extra de XML y XSLT (convirtiéndolos en una sola línea larga de código), ¿hay alguna otra manera de deshacerse de los espacios en blanco en la salida?
Editar: Aquí hay una pequeña muestra de XML:
<PHONEBOOK>
<LISTING>
<FIRST>John</FIRST>
<LAST>Smith</LAST>
<ADDRESS TYPE="Home">
<STREET>123 test</STREET>
<CITY>Sebastopol</CITY>
<STATE>California</STATE>
<ZIP>12345</ZIP>
</ADDRESS>
<PHONE>1-800-123-4567</PHONE>
<EMAIL>[email protected]</EMAIL>
<RELATION>Friend</RELATION>
</LISTING>
</PHONEBOOK>
Y aquí está el XSLT:
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/">
<xsl:for-each select="//LISTING">
<xsl:value-of select="RELATION" /><xsl:text>, </xsl:text>
<xsl:value-of select="FIRST" /><xsl:text>, </xsl:text>
<xsl:value-of select="LAST" /><xsl:text>, </xsl:text>
<xsl:if test="ADDRESS">
<xsl:for-each select="ADDRESS">
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" />,
</xsl:when>
<xsl:otherwise>
<xsl:text>Home </xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="STREET" />,
<xsl:value-of select="CITY" />,
<xsl:value-of select="STATE" />,
<xsl:value-of select="ZIP" />,
</xsl:for-each>
</xsl:if>
<xsl:for-each select="PHONE">
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" />
</xsl:when>
<xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
</xsl:choose>
<xsl:value-of select="." /><xsl:text >, </xsl:text>
</xsl:for-each>
<xsl:if test="EMAIL">
<xsl:for-each select="EMAIL">
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" /><xsl:text > </xsl:text>
</xsl:when>
<xsl:otherwise><xsl:text >Personal </xsl:text></xsl:otherwise>
</xsl:choose>
<xsl:value-of select="." /><xsl:text >, </xsl:text>
</xsl:for-each>
</xsl:if>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
En XSLT, el espacio en blanco se conserva por defecto, ya que puede ser información relevante.
La mejor manera de evitar el espacio en blanco no deseado en la salida no es crearlo en primer lugar. No hagas:
<xsl:template match="foo">
foo
</xsl:template>
porque eso es "/n··foo/n"
, desde el punto de vista del procesador. Más bien hacer
<xsl:template match="foo">
<xsl:text>foo</xsl:text>
</xsl:template>
El espacio en blanco en la hoja de estilo se ignora siempre que ocurra solo entre elementos XML. En pocas palabras: nunca use texto "desnudo" en ninguna parte de su código XSLT, enciérrelo siempre en un elemento.
Además, usando un método inespecífico:
<xsl:apply-templates />
es problemático, porque la regla XSLT predeterminada para los nodos de texto dice "cópialos a la salida". Esto también se aplica a los nodos de "espacio en blanco". Por ejemplo:
<xml>
<data> value </data>
</xml>
contiene tres nodos de texto:
-
"/n··"
(justo después de<xml>
) -
"·value·"
- "
/n"
(justo antes de</xml>
)
Para evitar que # 1 y # 3 entren furtivamente en el resultado (que es la razón más común para los espacios no deseados), puede anular la regla predeterminada para los nodos de texto al declarar una plantilla vacía:
<xsl:template match="text()" />
Todos los nodos de texto ahora están silenciados y la salida de texto debe crearse explícitamente:
<xsl:value-of select="data" />
Para eliminar el espacio en blanco de un valor, puede usar la función XSLT normalize-space()
:
<xsl:value-of select="normalize-space(data)" />
Pero cuidado, ya que la función normaliza cualquier espacio en blanco encontrado en la cadena, por ejemplo, "·value··1·"
se convertiría en "value·1"
.
Además, puede utilizar los elementos <xsl:strip-space>
y <xsl:preserve-space>
, aunque normalmente esto no es necesario (y personalmente, prefiero el manejo explícito de espacio en blanco como se indicó anteriormente).
En cuanto a eliminar pestañas pero retener líneas separadas, probé el siguiente enfoque XSLT 1.0, y funciona bastante bien. Su uso de la versión 1.0 o 2.0 depende en gran medida de la plataforma que esté utilizando. Parece que la tecnología .NET aún depende de XSLT 1.0, por lo que está limitado a plantillas extremadamente desordenadas (consulte a continuación). Si está utilizando Java o algo más, consulte el enfoque XSLT 2.0, mucho más limpio, que se encuentra en la parte inferior.
Estos ejemplos deben ser extendidos por usted para satisfacer sus necesidades específicas. Estoy usando pestañas aquí como ejemplo, pero esto debería ser lo suficientemente genérico como para ser extensible.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<text>
adslfjksdaf
dsalkfjdsaflkj
lkasdfjlsdkfaj
</text>
... y la plantilla XSLT 1.0 (requerida si usa .NET):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template name="search-and-replace">
<xsl:param name="input"/>
<xsl:param name="search-string"/>
<xsl:param name="replace-string"/>
<xsl:choose>
<xsl:when test="$search-string and
contains($input,$search-string)">
<xsl:value-of
select="substring-before($input,$search-string)"/>
<xsl:value-of select="$replace-string"/>
<xsl:call-template name="search-and-replace">
<xsl:with-param name="input"
select="substring-after($input,$search-string)"/>
<xsl:with-param name="search-string"
select="$search-string"/>
<xsl:with-param name="replace-string"
select="$replace-string"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$input"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text">
<xsl:call-template name="search-and-replace">
<xsl:with-param name="input" select="text()" />
<xsl:with-param name="search-string" select="''	''" />
<xsl:with-param name="replace-string" select="''''" />
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0 lo hace trivial con la función de replace
:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="text">
<xsl:value-of select="replace(text(), ''	'', '''')" />
</xsl:template>
</xsl:stylesheet>
La respuesta Myuseuse es incorrecta, todas las comas deben salir a través de la etiqueta ''texto''
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/PHONEBOOK">
<xsl:for-each select="LISTING">
<xsl:value-of select="RELATION" /><xsl:text>, </xsl:text>
<xsl:value-of select="FIRST" /><xsl:text>, </xsl:text>
<xsl:value-of select="LAST" /><xsl:text>, </xsl:text>
<xsl:for-each select="ADDRESS">
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" /><xsl:text>,</xsl:text>
</xsl:when>
<xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
</xsl:choose>
<xsl:value-of select="STREET/text()" /><xsl:text>,</xsl:text>
<xsl:value-of select="CITY/text()" /><xsl:text>,</xsl:text>
<xsl:value-of select="STATE/text()" /><xsl:text>,</xsl:text>
<xsl:value-of select="ZIP/text()" /><xsl:text>,</xsl:text>
</xsl:for-each>
<xsl:for-each select="PHONE">
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" />
</xsl:when>
<xsl:otherwise><xsl:text>Home </xsl:text></xsl:otherwise>
</xsl:choose>
<xsl:value-of select="." /><xsl:text >, </xsl:text>
</xsl:for-each>
<xsl:if test="EMAIL">
<xsl:for-each select="EMAIL">
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" /><xsl:text > </xsl:text>
</xsl:when>
<xsl:otherwise><xsl:text >Personal </xsl:text></xsl:otherwise>
</xsl:choose>
<xsl:value-of select="." /><xsl:text >, </xsl:text>
</xsl:for-each>
</xsl:if>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()|@*">
<xsl:text>-</xsl:text>
</xsl:template>
</xsl:stylesheet>
Modificar el código que utilizamos para formatear el archivo xml sin procesar al eliminar las líneas a continuación eliminará espacios en blanco adicionales en blanco agregados en Excel exportado.
Al formatear con un sistema de propiedades con sangría, se están agregando espacios en blanco adicionales.
Las líneas de comentarios se relacionan con el formato de xml como la línea siguiente y prueba.
xmlWriter.Formatting = System.Xml.Formatting.Indented;
Otros ya han señalado el problema general. El específico para su hoja de estilo es que olvidó <xsl:text>
para las comas:
<xsl:choose>
<xsl:when test="@TYPE">
<xsl:value-of select="@TYPE" />,
</xsl:when>
<xsl:otherwise>Home </xsl:otherwise>
</xsl:choose>
<xsl:value-of select="STREET" />,
<xsl:value-of select="CITY" />,
<xsl:value-of select="STATE" />,
<xsl:value-of select="ZIP" />,
Esto hace que los espacios en blanco que siguen cada coma sean significativos, y así termina en la salida. Si ajusta cada coma en <xsl:text>
, el problema desaparece.
Además, deshazte de ese disable-output-escaping
. No hace nada aquí, ya que no está generando XML.
Por defecto, las plantillas XSLT tienen <xsl:preserve-space>
set, lo que mantendrá espacios en blanco en su salida. Puede agregar <xsl:strip-space elements="*">
para indicarle dónde eliminar espacios en blanco.
Es posible que también deba incluir una directiva de espacio normalizado, así:
<xsl:template match="text()"><xsl:value-of select="normalize-space(.)"/></xsl:template>
Aquí hay un ejemplo para preservar / quitar espacio de las Escuelas W3 .