Pregunta

Tengo el siguiente bloque de código que recibe el nombre de los nodos abajo del árbol, como esto:

section/page/subPage

Pero me gustaría ser capaz de conseguir que reduce a lo siguiente (inventando):

section[@id='someId']/page/subPage[@user='UserA']/@title

I conocer el siguiente código de uno de estos mensajes StackOverflow:


<xsl:attribute name="path">
  <xsl:for-each select="ancestor-or-self::*">
    <xsl:if test="name() != 'root'">
      <xsl:value-of select="name()">
        <xsl:if test="not(position()=last())">
          <xsl:text>/</xsl:text>
        </xsl:if>
      </xsl:value-of>
    </xsl:if>
  </xsl:for-each>
</xsl:attribute>

Lo que me da el camino recto, pero me gustaría correr más lógica en él para que sea lo que incluía la @id (o atributo relevante), y tal vez algunas cosas más que no puedo pensar en este momento.

¿Cuál es la mejor manera de hacer esto?

He comprobado en funciones EXSLT, que pueden funcionar, pero tal vez ustedes ya han resuelto este problema de una manera mejor.

¿Alguna idea?

Estoy usando nokogiri de rubí para analizar el código XML / XSLT si eso ayuda.

un montón

Gracias, Lanza

¿Fue útil?

Solución

Esta solución hace lo que quiere:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" encoding="utf-8" omit-xml-declaration="yes" />

  <xsl:template match="/">
    <root>
      <xsl:attribute name="path">
        <xsl:apply-templates select="(//@title)[1]" mode="make-path" />
      </xsl:attribute>
    </root>
  </xsl:template>

  <xsl:template match="*|@*" mode="make-path">
    <xsl:apply-templates select="parent::*" mode="make-path" />
    <xsl:text>/</xsl:text>
    <xsl:apply-templates select="." mode="make-name" />
    <xsl:choose>
      <xsl:when test="self::section">
        <xsl:apply-templates select="@id" mode="make-predicate" />
      </xsl:when>
      <xsl:when test="self::subPage">
        <xsl:apply-templates select="@user" mode="make-predicate" />
      </xsl:when>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="*|@*" mode="make-predicate">
    <xsl:text>[</xsl:text>
    <xsl:apply-templates select="." mode="make-name" />
    <xsl:text> = '</xsl:text>
    <xsl:value-of select="." />
    <xsl:text>']</xsl:text>
  </xsl:template>

  <xsl:template match="*" mode="make-name">
    <xsl:value-of select="name()" />
  </xsl:template>

  <xsl:template match="@*" mode="make-name">
    <xsl:text>@</xsl:text>
    <xsl:value-of select="name()" />
  </xsl:template>

</xsl:stylesheet>

Cuando se aplica a

<section id="someId">
  <page>
    <subPage user="UserA" title="test" />
    <subPage user="UserB" title="blah" />
  </page>
  <page>
    <subPage user="UserC" title="fooh" />
  </page>
</section>

que se obtiene:

<root path="/section[@id = 'someId']/page/subPage[@user = 'UserA']/@title" />

El <xsl:choose> es el lugar configurable (añada tantas <xsl:when>s como desee):

<!-- test for element name -->
<xsl:when test="self::section">
  <!-- make predicates out of selected attributes -->
  <xsl:apply-templates select="@id" mode="make-predicate" />
</xsl:when>

También es posible:

<xsl:when test="self::section">
  <xsl:apply-templates select="@name|@category|subElement" mode="make-predicate" />
</xsl:when>

lo que llevaría a

<root path="/section[@name = 'someName'][@category = 'somecat'][subElement = 'xyz']/..." />

El único problema que veo es con los valores predicados que contienen comillas simples. Romperían el XPath.

Otros consejos

Para cada nodo ancestro puede bucle sobre todos los atributos con sencillo xsl: for-each

<xsl:for-each select="@*">

A continuación, puede utilizar los atributos para construir la cadena XPath

<xsl:for-each select="ancestor-or-self::*">
   <xsl:if test="name() != 'root'">
      <xsl:value-of select="name()"/>
      <xsl:if test="@*">
         <xsl:text>[</xsl:text>
         <xsl:for-each select="@*">
            <xsl:text>@</xsl:text>
            <xsl:value-of select="name()"/>
            <xsl:text>='</xsl:text>
            <xsl:value-of select="."/>
            <xsl:text>'</xsl:text>
            <xsl:if test="not(position()=last())">
               <xsl:text> and </xsl:text>
            </xsl:if>
         </xsl:for-each>
         <xsl:text>]</xsl:text>
      </xsl:if>
      <xsl:if test="not(position()=last())">
         <xsl:text>/</xsl:text>
      </xsl:if>
   </xsl:if>
</xsl:for-each>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top