xsl transformador strings online multiple concatenate concatenar xslt concatenation

transformador - Cadena concat XSLT, eliminar la última coma



xslt editor (6)

¿No tienes un valor que siempre estará ahí? Si lo haces, puedes darle la vuelta y poner comas delante de todo aparte del primer elemento (que sería tu valor que siempre está ahí).

Necesito construir una cadena usando XSLT y separar cada cadena con una coma, pero no incluir una coma después de la última cadena. En mi ejemplo a continuación, tendré una coma final si tengo un nodo de Distribución y no un nodo Nota, por ejemplo. No sé de todos modos para construir una cadena como una variable y luego truncar el último carácter en XSLT. También esto está usando el motor Microsoft XSLT.

Mi cadena =

<xsl:if test="Locality != ''''"> <xsl:value-of select="Locality"/>, </xsl:if> <xsl:if test="CollectorAndNumber != ''''"> <xsl:value-of select="CollectorAndNumber"/>, </xsl:if> <xsl:if test="Institution != ''''"> <xsl:value-of select="Institution"/>, </xsl:if> <xsl:if test="Distribution != ''''"> <xsl:value-of select="Distribution"/>, </xsl:if> <xsl:if test="Note != ''''"> <xsl:value-of select="Note"/> </xsl:if>

[Hombre, debe haber una mejor manera de ingresar a este cuadro de texto de pregunta :(]


Creo que podría ser útil mencionar que position () no funciona bien cuando uso una selección complicada que filtra algunos nodos, en ese caso surgió cuál es este truco:

puede definir una variable de cadena que contenga el valor de los nodos, separados por un carácter específico, y luego mediante str: tokenize () puede crear una lista de nodos completa cuya posición funciona bien con ella.

algo como esto:

<!-- Since position() doesn''t work as expected(returning node position of current node list), I got round it by a string variable and tokenizing it in which absolute position is equal to relative(context) position. --> <xsl:variable name="measObjLdns" > <xsl:for-each select="h:measValue[@measObjLdn=$currentMeasObjLdn]/h:measResults" > <xsl:value-of select="concat(.,''---'')"/> <!-- is an optional separator. --> </xsl:for-each> </xsl:variable> <xsl:for-each select="str:tokenize($measObjLdns,''---'')" ><!-- Since position() doesn''t work as expected(returning node position of current node list), I got round it by a string variable and tokenizing it in which absolute position is equal to relative(context) position. --> <xsl:value-of select="."></xsl:value-of> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if> </xsl:for-each> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if>


Esto sería un poco complicado, pero podría hacer el truco si solo hay unos pocos elementos como en su ejemplo:

<xsl:if test="Locality != ''''"> <xsl:value-of select="Locality"/> <xsl:if test="CollectorAndNumber != '''' or Institution != '''' or Distribution != '''' or Note != ''''"> <xsl:value-of select="'',''"/> </xsl:if> </xsl:if> <xsl:if test="CollectorAndNumber != ''''"> <xsl:value-of select="CollectorAndNumber"/> <xsl:if test="Institution != '''' or Distribution != '''' or Note != ''''"> <xsl:value-of select="'',''"/> </xsl:if> </xsl:if> <xsl:if test="Institution != ''''"> <xsl:value-of select="Institution"/> <xsl:if test="Distribution != '''' or Note != ''''"> <xsl:value-of select="'',''"/> </xsl:if> </xsl:if> <xsl:if test="Distribution != ''''"> <xsl:value-of select="Distribution"/> <xsl:if test="Note != ''''"> <xsl:value-of select="'',''"/> </xsl:if> </xsl:if> <xsl:if test="Note != ''''"> <xsl:value-of select="Note"/> </xsl:if>


Preferiría una plantilla de llamada corta para unir los valores de los nodos. Esto también funciona si falta un nodo en el medio de su lista concatenada, por ejemplo, Institution :

<xsl:template name="join"> <xsl:param name="list" /> <xsl:param name="separator"/> <xsl:for-each select="$list"> <xsl:value-of select="." /> <xsl:if test="position() != last()"> <xsl:value-of select="$separator" /> </xsl:if> </xsl:for-each> </xsl:template>

Aquí hay un breve ejemplo de cómo usarlo:

Documento de entrada de muestra:

<?xml version="1.0" encoding="utf-8"?> <items> <item> <Locality>locality1</Locality> <CollectorAndNumber>collectorAndNumber1</CollectorAndNumber> <Distribution>distribution1</Distribution> <Note>note1</Note> </item> <item> <Locality>locality2</Locality> <CollectorAndNumber>collectorAndNumber2</CollectorAndNumber> <Institution>institution2</Institution> <Distribution>distribution2</Distribution> <Note>note2</Note> </item> <item> <Locality>locality3</Locality> <CollectorAndNumber>collectorAndNumber3</CollectorAndNumber> <Institution>institution3</Institution> <Distribution>distribution3</Distribution> </item> </items>

Transformación XSL:

<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <summary> <xsl:apply-templates /> </summary> </xsl:template> <xsl:template match="item"> <item> <xsl:call-template name="join"> <xsl:with-param name="list" select="Locality | CollectorAndNumber | Institution | Distribution | Note" /> <xsl:with-param name="separator" select="'',''" /> </xsl:call-template> </item> </xsl:template> <xsl:template name="join"> <xsl:param name="list" /> <xsl:param name="separator"/> <xsl:for-each select="$list"> <xsl:value-of select="." /> <xsl:if test="position() != last()"> <xsl:value-of select="$separator" /> </xsl:if> </xsl:for-each> </xsl:template> </xsl:stylesheet>

Documento de salida generado:

<?xml version="1.0" encoding="utf-8"?> <summary> <item>locality1,collectorAndNumber1,distribution1,note1</item> <item>locality2,collectorAndNumber2,institution2,distribution2,note2</item> <item>locality3,collectorAndNumber3,institution3,distribution3</item> </summary>

NB: si estuvieras usando XSLT / XPath 2.0, entonces habría fn:string-join

fn:string-join**($operand1 as string*, $operand2 as string*) as string

que podría usarse de la siguiente manera:

fn:string-join({Locality, CollectorAndNumber, Distribution, Note}, ",")


Supongamos que tiene algo así como el siguiente XML de entrada:

<root> <record> <Locality>Locality</Locality> <CollectorAndNumber>CollectorAndNumber</CollectorAndNumber> <Institution>Institution</Institution> <Distribution>Distribution</Distribution> <Note>Note</Note> <OtherStuff>Unimportant</OtherStuff> </record> </root>

Entonces esta plantilla lo haría:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > <xsl:output method="text" /> <xsl:template match="record"> <xsl:variable name="values"> <xsl:apply-templates mode="concat" select="Locality" /> <xsl:apply-templates mode="concat" select="CollectorAndNumber" /> <xsl:apply-templates mode="concat" select="Institution" /> <xsl:apply-templates mode="concat" select="Distribution" /> <xsl:apply-templates mode="concat" select="Note" /> </xsl:variable> <xsl:value-of select="substring($values, 1, string-length($values) - 1)" /> <xsl:value-of select="''&#10;''" /><!-- LF --> </xsl:template> <xsl:template match="Locality | CollectorAndNumber | Institution | Distribution | Note" mode="concat"> <xsl:value-of select="." /> <xsl:text>,</xsl:text> </xsl:template> </xsl:stylesheet>

Salida en mi sistema:

Locality,CollectorAndNumber,Institution,Distribution,Note


Esto es muy fácil de lograr con XSLT ( no es necesario capturar los resultados en una variable, o usar plantillas especiales con nombre ):

I. XSLT 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= "Locality/text() | CollectorAndNumber/text() | Institution/text() | Distribution/text() | Note/text() " > <xsl:value-of select="."/> <xsl:if test="not(position() = last())">,</xsl:if> </xsl:for-each> </xsl:template> </xsl:stylesheet>

cuando esta transformación se aplica en el siguiente documento XML:

<root> <record> <Locality>Locality</Locality> <CollectorAndNumber>CollectorAndNumber</CollectorAndNumber> <Institution>Institution</Institution> <Distribution>Distribution</Distribution> <Note></Note> <OtherStuff>Unimportant</OtherStuff> </record> </root>

el resultado deseado es producido :

Locality,CollectorAndNumber,Institution,Distribution

Si los elementos buscados no se deben producir en orden de documentos (algo no requerido en la pregunta, pero planteado por Tomalak), sigue siendo bastante fácil y elegante lograr esto:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:param name="porderedNames" select="'' CollectorAndNumber Locality Distribution Institution Note ''"/> <xsl:template match="/*/*"> <xsl:for-each select= "*[contains($porderedNames, concat('' '',name(), '' ''))]"> <xsl:sort data-type="number" select="string-length( substring-before($porderedNames, concat('' '',name(), '' '') ) )"/> <xsl:value-of select="."/> <xsl:if test="not(position() = last())">,</xsl:if> </xsl:for-each> </xsl:template> </xsl:stylesheet>

Aquí los nombres de los elementos deseados y su orden deseada se proporcionan en el parámetro de cadena $porderedNames , que contiene una lista separada por espacios de todos los nombres deseados .

Cuando la transformación anterior se aplica en el mismo documento XML, se produce el resultado deseado :

CollectorAndNumber,Locality,Distribution,Institution

II. XSLT 2.0 :

En XSLT, esta tarea es incluso más simple ( nuevamente, no es necesaria ninguna función especial ):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:template match="/*/*"> <xsl:value-of separator="," select= "(Locality, CollectorAndNumber, Institution, Distribution, Note)[text()]" /> </xsl:template> </xsl:stylesheet>

Cuando esta transformación se aplica en el mismo documento XML, se produce el mismo resultado correcto :

Locality,CollectorAndNumber,Institution,Distribution

Tenga en cuenta que los elementos deseados se producirán en el orden deseado, porque estamos utilizando el tipo de secuencia XPath 2.0 (frente a la unión en la solución XSLT 1.0), que por definición contiene elementos en el orden deseado (especificado).