XSLT Agrupamiento de 3 niveles en los atributos

De acuerdo, sé que se han formulado y respondido variaciones sobre esto; Los he estado leyendo todo el día, pero todavía estoy atascado. Entonces, aquí va:

Necesito crear una lista resumida en HTML desde algún XML.

Dado este XML:

                 

Necesito emitir:

 
  1. Area 1:
    1. Unit 83:
      1. Part 9122 (foo)
      2. Part 9126 (bar)
    2. Unit 86:
      1. Part 8650 (baz)
    3. Unit 95:
      1. Part 7350 (meh)
  2. Area 2:
    1. Unit 26:
      1. Part 215 (quux)

Tengo funcionando la agrupación externa. Obtengo elementos de la lista de nivel superior para el Área 1 y 2. Pero no puedo obtener las secuencias de las Unidades en las Áreas. Tampoco obtengo salida ni repito el mismo valor. Ni siquiera he bajado al nivel de Parte 🙁

He estado trabajando en una hoja de estilos como esta:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     test grouping  
  1. Area :
    1. Unit :
      1. (Parts go here...)

¡Cualquier ayuda es muy apreciada!

Aquí está la solución de agrupación Muenchian que está buscando.

Yendo desde el XML original que me proporcionó, pensé que agrupar por AreaID sería suficiente, pero resulta que también se necesita una segunda agrupación por UnitID.

Aquí está mi solución XSLT 1.0 modificada. No es mucho más complejo que la solución original:

         
  • Como su XML no se encuentra, agregué un UnitID para agrupar en:

        

    Y aquí está el resultado:

     
    1. Area 1
      1. Unit 83
        1. Part 9122 (foo)
        2. Part 9126 (bar)
      2. Unit 86
        1. Part 8650 (baz)
        2. Part 8651 (zzz)
      3. Unit 95
        1. Part 7350 (meh)
    2. Area 2
      1. Unit 26
        1. Part 215 (quux)

    Como parece que tiene problemas con la tecla XSL, aquí mi bash de explicación:

    Una es absolutamente equivalente a la matriz asociativa (map, hash, como quiera que la llame) conocida en muchos lenguajes de progtwigción. Esta:

      

    genera una estructura de datos que podría expressse en JavaScript de esta manera:

     var kPlanByAreaAndUnit = { "1,83": ['array of all  nodes with @AreaID="1" and @UnitID="83"'], "1,86": ['array of all  nodes with @AreaID="1" and @UnitID="86"'], /* ... */ "1,95": ['array of all  nodes with @AreaID="1" and @UnitID="95"'] }; 

    La función para acceder a la estructura de datos se llama key() . Entonces, esta expresión XPath:

     key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID)) 

    es el equivalente lógico de (en JavaScript, de nuevo):

     kPlanByAreaAndUnit[this.AreaID + ',' + this.UnitID]; 

    devolver una matriz (un conjunto de nodos, más correctamente) de todos los nodos que coincidan con la cadena clave dada (la clave siempre es una cadena). Este conjunto de nodos se puede usar como cualquier otro conjunto de nodos en XSLT, es decir, como los que recupera a través de XPath “tradicional”. Esto significa que puede aplicarle condiciones (predicados):

      key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))[1]  key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))[Part] 

    o utilícelo como base para la navegación XPath:

      key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))/Part 

    y así. Esto también significa que podemos usarlo como una expresión “select” para , y podemos usarlo como base para agrupar. Lo que nos lleva al núcleo de la hoja de estilo anterior (si has entendido bien esta cuestión, también has entendido el rest de la solución):

     key('kPlanByArea', @AreaID)[ generate-id() = generate-id( key('kPlanByAreaAndUnit', concat(@AreaID, ',', @UnitID))[1] ) ] 

    En JavaScript nuevamente, esto podría expressse como:

     // the result will be a node-set, so we prepare an array var selectedNodes = []; // "key('kPlanByArea', @AreaID)" var nodeSet = kPlanByArea[this.AreaID]; // "[...]" - the [] actually triggers a loop that applies // the predicate expression to all nodes in the set, so we do: for (var i = 0; i < nodeSet.length; i++) { // use the current node for any calculations var c = nodeSet[i]; if ( // if the current node === the *first* node in kPlanByAreaAndUnit... generateId(c) == generateId(kPlanByAreaAndUnit[c.AreaID + ',' + c.UnitID][0]) ) { // ...include it in the resulting selection selectedNodes.push(c) } } 

    Una vez realizada la expresión, solo se seleccionan los nodos que son los primeros respectivos con una combinación dada de "ID de área, ID de unidad", efectivamente los hemos agrupado en su combinación "ID de área, ID de unidad".

    La aplicación de una plantilla a este conjunto de nodos hace que cada combinación aparezca solo una vez. Mi luego recupera la lista completa nuevamente para lograr un resultado completo para cada grupo.

    Espero que el uso de JavaScript para explicar el concepto sea una idea útil.

    No creo que deba usar la clave kUnitID en absoluto. En su lugar, reemplace la siguiente línea …

      

    ..con esta línea en su lugar, que debe recorrer todas las partes que coincidan con el ID de área actual

      

    Y dentro de este ciclo, para su código (Parts go here …) , puede simplemente pasar por encima de las partes

      
  • Part ()
  • Bueno, de momento abandoné las llaves y la agrupación Muenchian. Apenas lo entiendo, y piratearlo no produjo los resultados deseados. Entiendo recursividad, sin embargo, fui con este enfoque recursivo que produce el resultado deseado. Lo encontré en http://www.biglist.com/lists/xsl-list/archives/200412/msg00865.html

    El hilo de discusión advierte que el rendimiento sufre una gran entrada vs. el enfoque de Muenchian, y la solución a continuación es prolija y repetitiva (podría prolly refactorizarla para hacerla más pequeña y más difícil de entender 😉 pero 1) en realidad me funciona, y 2) para mi problema actual, los conjuntos de entrada son bastante pequeños, no más de una docena de nodos Parte de nivel inferior.

     < ?xml version="1.0" encoding="UTF-8"?>      test grouping   
  • Area :
  • Unit :
  • Part ()
  • Esto hace lo que quiere pero con recursión, no agrupación. Lo siento, todavía estoy aprendiendo cómo usar la agrupación:

            test grouping   
    1. Area :
      1. Unit :
        1. Part