2010-05-26 36 views
5

He hecho trampas cada vez que he necesitado hacer un conteo de líneas en XSLT usando JScript, pero en este caso no puedo hacer eso. Simplemente quiero escribir un contador de línea a lo largo de un archivo de salida. Este ejemplo básico tiene una solución sencilla:Contador de línea XSLT: ¿es tan difícil?

<xsl:for-each select="Records/Record"> 
    <xsl:value-of select="position()"/> 
</xsl:for-each> 

salida sería:

etc ...

Pero lo que si la estructura es más compleja con la anidados foreach:

<xsl:for-each select="Records/Record"> 
    <xsl:value-of select="position()"/> 
    <xsl:for-each select="Records/Record"> 
     <xsl:value-of select="position()"/> 
    </xsl:for-each> 
</xsl:for-each> 

Aquí, el foreach interior se acaba de reiniciar el contador (por lo que se obtiene 1, 1, 2, 3, 2, 1, 2, 3, 1, 2 etc.). ¿Alguien sabe cómo puedo sacar la posición en el archivo (es decir, un recuento de líneas)?

+0

excelente pregunta (1). Vea mi respuesta para una solución que produce números de línea para texto. –

Respuesta

5

Una línea en un archivo XML no es realmente lo mismo que un elemento. En su primer ejemplo, realmente no cuenta las líneas, sino la cantidad de elementos.

Un archivo XML podría tener este aspecto:

<cheeseCollection> 
<cheese country="Cyprus">Gbejna</cheese><cheese>Liptauer</cheese><cheese>Anari</cheese> 
</cheeseCollection> 

O exactamente el mismo archivo XML puede tener este aspecto:

<cheeseCollection> 
    <cheese 
     country="Cyprus">Gbejna</cheese> 
    <cheese>Liptauer</cheese> 
    <cheese>Anari</cheese> 
</cheeseCollection> 

cual el XSLT interpet exactamente lo mismo - no realmente molestarse con los saltos de línea.

Por lo tanto, es difícil mostrar los números de línea de la manera que desee con XSLT; no está diseñado para ese tipo de análisis.

Alguien me corrige si me equivoco, pero yo diría que necesitaría Javascript o algún otro lenguaje de scripting para hacer lo que quiera.

+1

Tiene razón: la "posición" (es decir, el número de línea) no tiene ningún significado en XML, ya que el espacio en blanco, incluidos los retornos de carro, no se considera parte del árbol de documentos. – GalacticCowboy

+1

@GalacticCowboy: los espacios en blanco * se * consideran como parte del árbol de documentos; se incluyen en los nodos de texto. – liori

+0

@liori: creo que se refería al espacio entre atributos: este espacio no es un objeto separado en el XDM y no se puede acceder mediante una expresión XPath –

6

Si bien es casi imposible marcar los números de línea para la serialización de un documento XML (porque este serialización en sí misma es ambigua), es perfectamente posible, y easy, numerar las líneas de texto normal.

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:call-template name="numberLines"/> 
</xsl:template> 

<xsl:template name="numberLines"> 
    <xsl:param name="pLastLineNum" select="0"/> 
    <xsl:param name="pText" select="."/> 

    <xsl:if test="string-length($pText)"> 
    <xsl:value-of select="concat($pLastLineNum+1, ' ')"/> 

    <xsl:value-of select="substring-before($pText, '&#xA;')"/> 
    <xsl:text>&#xA;</xsl:text> 

    <xsl:call-template name="numberLines"> 
    <xsl:with-param name="pLastLineNum" 
     select="$pLastLineNum+1"/> 
    <xsl:with-param name="pText" 
     select="substring-after($pText, '&#xA;')"/> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 
</xsl:stylesheet> 

cuando se aplica en este documento XML:

<t>The biggest airlines are imposing "peak travel surcharges" 
this summer. In other words, they're going to raise fees 
without admitting they're raising fees: Hey, it's not a $30 
price hike. It's a surcharge! This comes on the heels of 
checked-baggage fees, blanket fees, extra fees for window 
and aisle seats, and "snack packs" priced at exorbitant 
markups. Hotels in Las Vegas and elsewhere, meanwhile, are 
imposing "resort fees" for the use of facilities (in other 
words, raising room rates without admitting they're 
raising room rates). The chiseling dishonesty of these 
tactics rankles, and every one feels like another nail in 
the coffin of travel as something liberating and 
pleasurable. 
</t> 

produce la deseada línea de numeración de:

1 The biggest airlines are imposing "peak travel surcharges" 
2 this summer. In other words, they're going to raise fees 
3 without admitting they're raising fees: Hey, it's not a $30 
4 price hike. It's a surcharge! This comes on the heels of 
5 checked-baggage fees, blanket fees, extra fees for window 
6 and aisle seats, and "snack packs" priced at exorbitant 
7 markups. Hotels in Las Vegas and elsewhere, meanwhile, are 
8 imposing "resort fees" for the use of facilities (in other 
9 words, raising room rates without admitting they're 
10 raising room rates). The chiseling dishonesty of these 
11 tactics rankles, and every one feels like another nail in 
12 the coffin of travel as something liberating and 
13 pleasurable. 
3

Gracias por las respuestas chicos - sí, está totalmente en lo cierto, alguna función externa es la única forma de obtener este comportamiento en XSLT.Para los que buscan, así es como lo hice cuando se utiliza un compilado transformada en .Net 3.5:

Crear una clase de ayuda para su función (s)

/// <summary> 
/// Provides functional support to XSLT 
/// </summary> 
public class XslHelper 
{ 
    /// <summary> 
    /// Initialise the line counter value to 1 
    /// </summary> 
    Int32 counter = 1; 

    /// <summary> 
    /// Increment and return the line count 
    /// </summary> 
    /// <returns></returns> 
    public Int32 IncrementCount() 
    { 
     return counter++; 
    } 
} 

Añadir una instancia a una lista de argumentos para XSLT

XslCompiledTransform xslt = new XslCompiledTransform(); 
xslt.Load(XmlReader.Create(s)); 
XsltArgumentList xslArg = new XsltArgumentList(); 
XslHelper helper = new XslHelper(); 
xslArg.AddExtensionObject("urn:helper", helper); 
xslt.Transform(xd.CreateReader(), xslArg, writer); 

Úselo en que XSLT

poner esto en el elemento de declaración hoja de estilo:

xmlns:helper="urn:helper" 

continuación, utilice este modo:

<xsl:value-of select="helper:IncrementCount()" /> 
1

Generalmente, position() se refiere al número del nodo actual con respecto a todo el lote de nodos que se está procesando actualmente.

Con su ejemplo "anidado para cada uno", la numeración consecutiva puede lograrse fácilmente cuando deja de anidar para cada constructo y simplemente selecciona todos los elementos deseados a la vez.

Con este XML:

<a><b><c/><c/></b><b><c/></b></a> 

un bucle construyen como esto

<xsl:for-each "a/b"> 
    <xsl:value-of select="position()" /> 
    <xsl:for-each "c"> 
    <xsl:value-of select="position()" /> 
    </xsl:for-each> 
</xsl:for-each> 

dará lugar a

11221 
bccbc // referred-to nodes 

pero usted puede simplemente hacer esto en su lugar:

<xsl:for-each "a/b/c"> 
    <xsl:value-of select="position()" /> 
</xsl:for-each> 

y que se obtendría

123 
ccc // referred-to nodes 
Cuestiones relacionadas