string - length - ¿Cómo formatear una cadena al caso de Pascal en XSLT?
substring length xsl (5)
Estoy tratando de formatear cadenas en XSLT que deben estar en el caso Pascal para usarlas apropiadamente para la aplicación con la que estoy trabajando.
Por ejemplo:
this_text se convertiría en ThisText
this_long_text se convertiría en ThisLongText
¿Es posible configurar esto donde puedo enviar una entrada al formato para no tener que volver a crear el formato varias veces?
Esta transformación :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vLower" select=
"''abcdefghijklmnopqrstuvwxyz''"/>
<xsl:variable name="vUpper" select=
"''ABCDEFGHIJKLMNOPQRSTUVWXYZ''"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText" select="concat(., ''_'')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="Pascalize">
<xsl:param name="pText"/>
<xsl:if test="$pText">
<xsl:value-of select=
"translate(substring($pText,1,1), $vLower, $vUpper)"/>
<xsl:value-of select="substring-before(substring($pText,2), ''_'')"/>
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText"
select="substring-after(substring($pText,2), ''_'')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
cuando se aplica en este documento XML :
<t>
<a>this_text</a>
<b>this_long_text</b>
</t>
produce el resultado deseado :
<t>
<a>ThisText</a>
<b>ThisLongText</b>
</t>
Por cierto, esto es camelCase y esta es PascalCase
Gracias a Dimitre, pude llegar la mayor parte del camino hasta allí. Cuando ejecuté mis cadenas a través de la plantilla Pascalize, el bit después del último ''_'' fue cortado. Probablemente haya una forma más limpia de hacerlo, pero aquí está el código que utilicé:
<xsl:template name="Pascalize">
<xsl:param name="pText"/>
<xsl:if test="$pText">
<xsl:value-of select="translate(substring($pText,1,1), $vLower, $vUpper)"/>
<xsl:value-of select="substring-before(substring($pText,2), ''_'')"/>
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText" select="substring-after(substring($pText,2), ''_'')"/>
</xsl:call-template>
<xsl:call-template name="GrabLastPart">
<xsl:with-param name="pText" select="$pText"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="GrabLastPart">
<xsl:param name="pText"/>
<xsl:choose>
<xsl:when test="contains($pText, ''_'')">
<xsl:call-template name="GrabLastPart">
<xsl:with-param name="pText" expr="substring-after($pText, ''_'')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($pText, 2)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Esta versión funcionó para mí. Agregué una opción que arroja "el resto" de la cadena cuando ya no hay barras debajo de la barra.
<xsl:variable name="vLower" select="''abcdefghijklmnopqrstuvwxyz''"/>
<xsl:variable name="vUpper" select="''ABCDEFGHIJKLMNOPQRSTUVWXYZ''"/>
<xsl:template name="Pascalize">
<xsl:param name="pText" />
<xsl:if test="$pText">
<xsl:value-of select="translate(substring($pText,1,1), $vLower, $vUpper)" />
<xsl:choose>
<xsl:when test="contains($pText, ''_'')">
<xsl:value-of select="substring-before(substring($pText,2), ''_'')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($pText,2)" />
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="Pascalize">
<xsl:with-param name="pText" select="substring-after(substring($pText,2), ''_'')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Además, en caso de que alguien venga aquí buscando el proceso inverso (que casualmente requiriera hoy y no podría encontrar ni un solo ejemplo de ningún lugar) ...
<xsl:variable name="vLower" select="''abcdefghijklmnopqrstuvwxyz''"/>
<xsl:variable name="vUpper" select="''ABCDEFGHIJKLMNOPQRSTUVWXYZ''"/>
<xsl:template name="TitleCase">
<xsl:param name="pText" />
<xsl:call-template name="TitleCase_recurse">
<xsl:with-param name="pText" select="concat(translate(substring($pText,1,1), $vLower, $vUpper), substring($pText,2))" />
</xsl:call-template>
</xsl:template>
<xsl:template name="TitleCase_recurse">
<xsl:param name="pText" />
<xsl:if test="string-length($pText) > 1">
<xsl:if test="not(substring($pText,1,1) = '' '' and substring($pText,1,1) = '' '')">
<xsl:value-of select="substring($pText,1,1)" />
</xsl:if>
<xsl:if test="translate(substring($pText,1,1), $vLower, $vUpper) != substring($pText,1,1)">
<xsl:if test="translate(substring($pText,2,1), $vLower, $vUpper) = substring($pText,2,1)">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:if>
<xsl:call-template name="TitleCase_recurse">
<xsl:with-param name="pText" select="substring($pText,2)" />
</xsl:call-template>
</xsl:if>
<xsl:if test="string-length($pText) = 1">
<xsl:value-of select="$pText" />
</xsl:if>
</xsl:template>
Me encanta cuando mi cerebro subconsciente aparece una respuesta unas horas después de que me haya rendido por completo conscientemente. ;-)
Estaba tratando de lograr el "pascalizing" con la siguiente llamada de función XLST:
<xsl:value-of select="fn:replace(@name,''_(/w{1})'',''/U$1'')"/>
Desafortunadamente, el procesador arroja el mensaje de error "Cadena de reemplazo inválida en replace (): / character debe ir seguida por / o $"
el problema es el modificador / U que se supone que hace la conversión en mayúsculas del patrón coincidente. Si lo cambio a
<xsl:value-of select="fn:replace(@name,''_(/w{1})'',''//U$1'')"/>
la cadena de salida contiene la secuencia ''/ U'' porque ahora está en blanco, pero no quiero escapar, quiero que sea efectiva ;-). Hice la prueba
<xsl:value-of select="fn:replace(@name,''_(/w{1})'',''$1'')"/>
(sin convertir el partido a mayúsculas) y eso funciona bien. Pero, por supuesto, no hace uppercasing, simplemente elimina guiones bajos y reemplaza la letra después del guión bajo por sí mismo en lugar de capitalizarlo. ¿Estoy haciendo algo mal aquí o el modificador / U simplemente no es compatible con la implementación de expresiones regulares de mi procesador XSLT?
Aquí, dos años después del hecho, es una solución XSLT 2.0:
<xsl:function name="fn:pascal-case">
<xsl:param name="string"/>
<xsl:value-of select="string-join(for $s in tokenize($string,''/W+'') return concat(upper-case(substring($s,1,1)),substring($s,2)),'''')"/>
</xsl:function>
Pascalizará ''this_long_text'' o ''this-long-text'' a ''ThisLongText'' porque rompe cualquier carácter que no sea de palabra.
En los sabores de expresiones regulares con los que estoy más familiarizado (perl, pcre, etc.), un guión bajo se considera parte de la clase de caracteres ''/ w'' (por lo tanto, no forma parte de / W), pero para XSLT 2.0 se usan los tipos de datos XSD ( http://www.w3.org/TR/xmlschema-2/) y ''/ w'' se define como:
[#x0000-#x10FFFF]-[/p{P}/p{Z}/p{C}] (all characters except the set of "punctuation", "separator" and "other" characters)
así que ''/ W'' incluye un guión bajo.