online - xpath xml
¿Cómo puedo generar una lista separada por comas con XSLT/XPath? (7)
Teniendo en cuenta estos datos XML:
<root> <item>apple</item> <item>orange</item> <item>banana</item> </root>
Puedo usar este marcado XSLT:
... <xsl:for-each select="root/item"> <xsl:value-of select="."/>, </xsl:for-each> ...
para obtener este resultado:
manzana, naranja, plátano,
pero ¿cómo puedo producir una lista donde la última coma no está presente? Supongo que se puede hacer haciendo algo como:
... <xsl:for-each select="root/item"> <xsl:value-of select="."/> <xsl:if test="...">,</xsl:if> </xsl:for-each> ...
pero, ¿cuál debería ser la expresión de prueba?
Necesito alguna forma de averiguar cuánto tiempo dura la lista y dónde estoy actualmente en la lista, o, alternativamente, si actualmente estoy procesando el último elemento de la lista (lo que significa que no me importa cuánto tiempo o qué la posición actual es).
Esta es la forma en que lo conseguí trabajando para mí. Probé esto en tu lista:
<?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="root">
<xsl:call-template name="comma-join"><xsl:with-param name="list" select="item"/></xsl:call-template>
</xsl:template>
<xsl:template name="comma-join">
<xsl:param name="list" />
<xsl:for-each select="$list">
<xsl:value-of select="." />
<xsl:if test="position() != last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Este es un patrón bastante común:
<xsl:for-each select="*">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
Para una opción XSLT 2.0, puede usar el atributo separator
en xsl:value-of
.
Este xsl:value-of
:
<xsl:value-of select="/root/item" separator=", "/>
produciría esta salida:
apple, orange, banana
También podría usar más que solo una coma para un separador. Por ejemplo, esto:
<xsl:text>''</xsl:text>
<xsl:value-of select="/root/item" separator="'', ''"/>
<xsl:text>''</xsl:text>
Produciría el siguiente resultado:
''apple'', ''orange'', ''banana''
Otra opción de XSLT 2.0 es string-join()
...
<xsl:value-of select="string-join(/*/item,'', '')"/>
Robert dio la respuesta classis not(position() = last())
. Esto requiere que procese toda la lista de nodos actual para obtener el tamaño de contexto, y en documentos de entrada grandes, esto podría hacer que la conversión consuma más memoria. Por lo tanto, normalmente invierto la prueba para ser lo primero
<xsl:for-each select="*">
<xsl:if test="not(position() = 1)>, </xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
Un simple one-liner XPath 1.0 :
concat(., substring('','', 2 - (position() != last())))
Ponlo en esta transformación:
<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="*">
<xsl:value-of select=
"concat(., substring('','', 2 - (position() != last())))"
/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
y aplicarlo al documento XML :
<root>
<item>apple</item>
<item>orange</item>
<item>banana</item>
</root>
para obtener el resultado deseado:
apple,orange,banana
EDITAR:
Aquí hay un comentario de Robert Rossney a esta respuesta:
Es un código bastante opaco para que un humano lo lea. Requiere que conozca dos cosas no obvias sobre XSLT: 1) qué hace la función de subcadena si su índice está fuera de rango y 2) que los valores lógicos se pueden convertir implícitamente a valores numéricos.
y aquí está mi respuesta :
Chicos, nunca se avergüencen de aprender algo nuevo. De hecho, esto es de lo que se trata , ¿verdad? :)
Observe las position()
, count()
y last()
; por ejemplo, test="position() < last()"
.
<xsl:if test="following-sibling::*">,</xsl:if>
o (quizás más eficiente, pero tendría que probar):
<xsl:for-each select="*[1]">
<xsl:value-of select="."/>
<xsl:for-each select="following-sibling::*">
<xsl:value-of select="concat('','',.)"/>
</xsl:for-each>
</xsl:for-each>