Announcement

Collapse
No announcement yet.

Dynamisches Array erstellen XSLT 1.0

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Dynamisches Array erstellen XSLT 1.0

    Hallo Forum,

    ich versuche gerade ein dynamisches Array zu erzeugen, im Web finde ich leider nur Artikel die das Loopen eines solchen Arrays aufzeigen.

    In meinem XML-Dokument finde ich eine Anzahl x von folgenden Tags innerhalb eines übergeordneten Elements:

    Code:
    <PathPoint Anchor="1234" />
    Diese muss ich anschließend mit einem statischen Wert verrechnen. Wie fülle ich die unbekannte Anzahl von //PathPoint/@Anchor in eine Variable, die ich loopen kann, verrechne und dann wieder in ein neues dynamisches Array speichere? Quasi sowas:

    Code:
    <!-- PSEUDO-CODE -->
    <xsl:variable name="array_ausgangswert">
       <xsl:for-each select="//PathPoint">
          <xsl:value-of select="@Anchor" />
       </xsl:for-each>
    </xsl:variable>
    
    <xsl:variable name="array_endwert">
       <xsl:for-each select="$array_ausgangswert">
          <xsl:value-of select=". + $wert" />
       </xsl:for-each>
    <xsl:variable>
    Ich danke für Eure Hilfe. Hoffentlich geht das mit XSLT 1.0.

    Gruss
    Dom

  • #2
    XSLT kennt das Konzept Array nicht. Unter XSLT 2.0 lassen sich ggf. Collections der Form (…, …, …) nutzen, deren Mitglieder man via Index [1] usw. abfragen kann. Zudem stellt XSLT 3.0 Maps zur Verfügung, welche sich Array-artig (assoziativ) ansprechen lassen. Im vorliegenden Fall entsteht nur ein einfacher String. Verwende ein passendes Trennzeichen, etwa "|" und gehe den String mittels String-Funktionen rekursiv durch. Alternativ bietet sich str:tokenize() aus EXSLT an.

    Hier ein Ansatz:

    Test-XML-Dokument:
    [Highlight=XML]<?xml version="1.0" encoding="UTF-8"?>
    <root>
    <PathPoint Anchor="1234"/>
    <PathPoint Anchor="2345"/>
    <PathPoint Anchor="3456"/>
    <PathPoint Anchor="4567"/>
    <PathPoint Anchor="5678"/>
    </root>[/Highlight]

    Dieses Stylesheet erzeugt den mit "|" versehenen Ausgangs-String und addiert die globale Variable 23 auf die einzelnen String-Teile und baut den Ergebnis-String wiederum mit "|"-Trenner zusammen:

    [Highlight=XML]<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:variable name="wert" select="23"/>

    <xsl:template match="root">
    <xsl:variable name="array_ausgangswert">
    <xsl:for-each select="//PathPoint">
    <xsl:value-of select="@Anchor"/>
    <xsl:if test="position() != last()">|</xsl:if>
    </xsl:for-each>
    </xsl:variable>

    <!-- <xsl:value-of select="$array_ausgangswert"/> -->
    <!-- $array_ausgangswert: 1234|2345|3456|4567|5678 -->

    <xsl:call-template name="strcode">
    <xsl:with-param name="array_ausgangswert" select="$array_ausgangswert"/>
    <xsl:with-param name="array_endwert" select="''"/>
    </xsl:call-template>
    </xsl:template>

    <xsl:template name="strcode">
    <xslaram name="array_ausgangswert"/>
    <xslaram name="array_endwert"/>

    <xsl:choose>
    <xsl:when test="string-length($array_ausgangswert) > 0">
    <xsl:variable name="str_before">
    <xsl:choose>
    <xsl:when test="contains($array_ausgangswert, '|')">
    <xsl:value-of select="substring-before($array_ausgangswert, '|')"/>
    </xsl:when>
    <xsltherwise>
    <xsl:value-of select="$array_ausgangswert"/>
    </xsltherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="str_after" select="substring-after($array_ausgangswert, '|')"/>
    <xsl:call-template name="strcode">
    <xsl:with-param name="array_ausgangswert" select="$str_after"/>
    <xsl:with-param name="array_endwert">
    <xsl:value-of select="concat($array_endwert, $str_before + $wert)"/>
    <xsl:if test="contains($array_ausgangswert, '|')">|</xsl:if>
    </xsl:with-param>
    </xsl:call-template>
    </xsl:when>
    <xsltherwise>
    <xsl:value-of select="$array_endwert"/> <!-- $array_endwert: 1257|2368|3479|4590|5701 -->
    </xsltherwise>
    </xsl:choose>
    </xsl:template>

    </xsl:stylesheet>[/Highlight]

    $array_endwert lässt sich nun weiterverarbeiten. Mit XSLT 2.0 ließe sich übrigens die Nutzung von xsl:call-template durch die XPath-Funktion fn:tokenize() vereinfachen.

    Comment


    • #3
      Hallo Thomas,

      danke für Deine Hilfe, im Prinzip verstehe ich nun was Du da machst, aber ich glaube dies nicht anwenden zu können. Ich habe quasi keine Einzeltemplates, ich müsste sehr weit ausholen, wenn ich dies erklären müsste. Kurzgefasst durchlaufe ich eine XML in der grafische Objekte (Rechtecke, Polygone, Kreise, Ellipsen) mit oder ohne Text oder Bild vorkommen - dabei ist es wichtig das diese der Reihe nach von unten nach oben dargestellt werden und im HTML Baum auftauchen (das mache ich mit einem permanenten z-index +1). Hier mal ein verkürzter Auszug, wie ich mir das o.g. Problem vorstellen könnte, leider werden keine Linien für den Canvas ausgegeben:

      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:when test="self::Polygon">
          <xsl:variable name="ypositions-array">
              <xsl:for-each select="//Properties/PathGeometry/GeometryPathType/PathPointArray/PathPointType">
                  <xsl:value-of select="str:tokenize(@Anchor,' ')[number(2)]" />
                  <xsl:if test="position() != last()">|</xsl:if>
              </xsl:for-each>
          </xsl:variable>
          <xsl:variable name="xpositions-array">
              <xsl:for-each select="//Properties/PathGeometry/GeometryPathType/PathPointArray/PathPointType">
                  <xsl:value-of select="str:tokenize(@Anchor,' ')[number(1)]" />
                  <xsl:if test="position() != last()">|</xsl:if>
              </xsl:for-each>
          </xsl:variable>
          <xsl:variable name="transformationData">
              <xsl:value-of select="@ItemTransform"/>
          </xsl:variable>
          <xsl:variable name="transformations">
              <transformationY>
                  <xsl:value-of select="str:tokenize($transformationData,' ')[number(6)]" />
              </transformationY>
              <transformationX>
                  <xsl:value-of select="str:tokenize($transformationData,' ')[number(5)]" />
              </transformationX>
              <rotation>
                  <!--<xsl:value-of select="math:asin(number(str:tokenize($transformationData,' ')[number(2)]) * 180 div 3.141592654)" />-->
                  <xsl:value-of select="math:asin(str:tokenize($transformationData,' ')[number(2)]) * 180 div 3.141592654" />
              </rotation>
          </xsl:variable>
          <xsl:variable name="ycoordinates">
              <xsl:for-each select="str:tokenize(ypositions-array, '|')">
                  <xsl:choose>
                      <xsl:when test="($FacingPages)='false'">
                          <xsl:value-of select="((exsl:node-set($pageDimensions)/width) div 2) + . + (exsl:node-set($transformations)/transformationY)"/>
                          <xsl:if test="position() != last()">|</xsl:if>
                      </xsl:when>
                      <xsl:when test="($FacingPages)='true' and ($BindingLocation)!=0">
                          <xsl:value-of select="(exsl:node-set($bindingRelations)/widthBeforeBinding) + . + (exsl:node-set($transformations)/transformationY)"/>
                          <xsl:if test="position() != last()">|</xsl:if>
                      </xsl:when>
                      <xsl:when test="($FacingPages)='true' and ($BindingLocation)=0">
                          <xsl:value-of select="((exsl:node-set($pageDimensions)/width) div 2) + . + (exsl:node-set($transformations)/transformationY)"/>
                          <xsl:if test="position() != last()">|</xsl:if>
                      </xsl:when>
                  </xsl:choose>
              </xsl:for-each>
          </xsl:variable>
          <xsl:variable name="xcoordinates">
              <xsl:for-each select="str:tokenize(xpositions-array, '|')">
                  <xsl:choose>
                      <xsl:when test="($FacingPages)='false'">
                          <xsl:value-of select="((exsl:node-set($pageDimensions)/width) div 2) + . + (exsl:node-set($transformations)/transformationX)"/>
                          <xsl:if test="position() != last()">|</xsl:if>
                      </xsl:when>
                      <xsl:when test="($FacingPages)='true' and ($BindingLocation)!=0">
                          <xsl:value-of select="(exsl:node-set($bindingRelations)/widthBeforeBinding) + . + (exsl:node-set($transformations)/transformationX)"/>
                          <xsl:if test="position() != last()">|</xsl:if>
                      </xsl:when>
                      <xsl:when test="($FacingPages)='true' and ($BindingLocation)=0">
                          <xsl:value-of select="((exsl:node-set($pageDimensions)/width) div 2) + . + (exsl:node-set($transformations)/transformationX)"/>
                          <xsl:if test="position() != last()">|</xsl:if>
                      </xsl:when>
                  </xsl:choose>
              </xsl:for-each>
          </xsl:variable>
          <xsl:choose>
              <xsl:when test="descendant::Image">
                  <xsl:variable name="imageId">
                      <xsl:value-of select="Image/Link/@Self"/>
                  </xsl:variable>
                  <xsl:variable name="imagePath">
                      <xsl:value-of select="Image/Link/@LinkResourceURI"/>
                  </xsl:variable>
                  <xsl:variable name="imageName">
                      <xsl:value-of select="str:tokenize($imagePath,'/')[last()]"/>
                  </xsl:variable>
                  <script>
                      [...]
      
                      ctx.beginPath();
                      </xsl:text>
                      <xsl:for-each select="str:tokenize($ycoordinates, '|')">
                          <xsl:variable name="pos" select="position()"/>
                          <xsl:choose>
                              <xsl:when test="position() = 1">
                                  <xsl:text>ctx.moveTo(</xsl:text>
                                  <xsl:value-of select="str:tokenize($xcoordinates, '|')[position()=$pos]"/>
                                  <xsl:text>,</xsl:text>
                                  <xsl:value-of select="."/>
                                  <xsl:text>);
                                  </xsl:text>
                              </xsl:when>
                              <xsl:otherwise>
                                  <xsl:text>ctx.lineTo(</xsl:text>
                                  <xsl:value-of select="str:tokenize($xcoordinates, '|')[position()=$pos]"/>
                                  <xsl:text>,</xsl:text>
                                  <xsl:value-of select="."/>
                                  <xsl:text>);
                                  </xsl:text>
                              </xsl:otherwise>    
                          </xsl:choose>
                      </xsl:for-each>
                      <xsl:text>ctx.lineWidth = </xsl:text>
                      <xsl:value-of select="$strokeWeight"/>
                      <xsl:text>;
                      ctx.closePath();
      
                      [...]
                  </script>
                  <canvas id="{$imageId}" style="position:relative; z-index: +1;"></canvas>
              </xsl:when>
          </xsl:choose>
      </xsl:when>
      Danke und Gruss
      Dom

      Comment


      • #4
        Kann ich so nicht nachvollziehen. Was mich aber wundert, ist die Zuweisung von z-index: +1. Soll dieser Wert für n canvas-Elemente hochgezählt werden? Der Defaultwert ist ja bereits 1 und +1 bewirkt hier also nichts bzw. mehrere canvas-Elemente liegen auf derselben Ebene.

        Comment


        • #5
          Guten Morgen,

          ich fasse mich mal kurz. Im Prinzip habe ich es anscheinend doch nicht verstanden. Mein XSL sah bis gerade folgende Situation vor (verkürzter Code):

          Code:
          <xsl:template match="/">
               <xsl:apply-templates/>
          </xsl:template>
          <xsl:template match="idPkg:Spread">
             <xsl:variable name="Musterseiten_ID">
             [...]
             </xsl:variable>
             <xsl:for-each select="document(Pfad + MusterseitenID)">
               <xsl:choose>
                  <xsl:when test="self::TextFrame">
                  [...]
                  </xsl:when>
                  <xsl:when test="self::Rectangle">
                  [...]
                  </xsl:when>
                  <xsl:when test="self::Polygon">
                  [...]
                  </xsl:when>
             </xsl:choose>
             </xsl:for-each>
             <xsl:choose>
               <xsl:when test="self::TextFrame">
               [...]
               </xsl:when>
               <xsl:when test="self::Rectangle">
               [...]
               </xsl:when>
               <xsl:when test="self::Polygon">
               [...]
               </xsl:when>
             </xsl:choose>
          </xsl:template>
          Ich habe also alles in einem Template abgefrühstückt und wusste dann nicht wie ich Deinen Code integrieren kann. Bzgl. des z-index ist die Regel, dass der weiße Hintergrund (ein leerer Container mit color: #fff) den z-index = 1 bekommt, alle folgenden Elemente (TextFrame, Rectangle, Polygon) bekommen z-index = +1, weil die Reihenfolge deren Vorkommen im XML der Ebene in Dokument entspricht.

          Soviel zur Klärung, ich habe das XSL nun umgestellt. In etwa so:

          Code:
          <xsl:template match="/">
               <xsl:apply-templates/>
          </xsl:template>
          <xsl:template match="idPkg:Spread">
               <xsl:variable name="Musterseiten_ID">
                [...]
               </xsl:variable>
               <xsl:for-each select="document(Pfad + MusterseitenID)">
                    <xsl:call-template name="TextFrame">
                    <xsl:call-template name="Rectangle">
                    <xsl:call-template name="Polygon">
               </xsl:for-each>
               <xsl:call-template name="TextFrame">
               <xsl:call-template name="Rectangle">
               <xsl:call-template name="Polygon">
          </xsl:template>
          <xsl:template name="TextFrame">
             <xsl:for-each select="//TextFrame">
                  <xsl:variable name="layer">
                       <xsl:value-of select="position()"/>
                  </xsl:variable>
                  <div style="position:relative; z-index: {$layer}">[...]</div>
             </xsl:for-each>
             <xsl:for-each select="//Rectangle">
                  <xsl:variable name="layer">
                       <xsl:value-of select="position()"/>
                  </xsl:variable>
                  <div style="position:relative; z-index: {$layer}">[...]</div>
             </xsl:for-each>
             <xsl:for-each select="//Polygon">
                  <xsl:variable name="layer">
                       <xsl:value-of select="position()"/>
                  </xsl:variable>
                  <div style="position:relative; z-index: {$layer}">[...]</div>
             </xsl:for-each>
          </xsl:template>
          Jetzt ist das XSL Skript 50% kürzer und ich positioniere den z-index über die position() im XML. Zudem habe ich Deinen Code für die X-Werte und Y-Werte der Eckpunkte des Polygons eingebaut. Dennoch bleiben zwei Fragen über:

          Wie durchlaufe ich das Quasi-Array der Endwerte? Hier mein Beispiel mit den Y-Werten:

          Code:
          <xsl:for-each select="str:tokenize($array_yendwert, '|')">
                                      <xsl:variable name="pos" select="position()"/>
                                      <xsl:choose>
                                          <xsl:when test="position() = 1">
                                              <xsl:text>ctx.moveTo(</xsl:text>
                                              <xsl:value-of select="str:tokenize($array_xendwert, '|')[position()=$pos]"/>
                                              <xsl:text>,</xsl:text>
                                              <xsl:value-of select="."/>
                                              <xsl:text>);
                                              </xsl:text>
                                          </xsl:when>
                                          <xsl:otherwise>
                                              <xsl:text>ctx.lineTo(</xsl:text>
                                              <xsl:value-of select="str:tokenize($array_xendwert, '|')[position()=$pos]"/>
                                              <xsl:text>,</xsl:text>
                                              <xsl:value-of select="."/>
                                              <xsl:text>);
                                              </xsl:text>
                                          </xsl:otherwise>    
                                      </xsl:choose>
          </xsl:for-each>
          Die zweite Frage ist bzgl. der position(). Wie o.a. finde ich natürlich nur die xte Position eines bestimmten Typs (TextFrame, Rectangle oder Polygon). Ich bräuchte aber die Position unabhängig vom Typ, sowas: "parent::*[position()]".

          Ich danke Dir für Deine kompetente Hilfe.
          Gruss
          Dominik

          Comment


          • #6
            Ohne testfähigen Code kann ich nur im Trüben fischen:

            1) Das Quasi-Array der Endwerte wird doch bereits durchlaufen, hier würde [$pos] statt [position() = $pos] reichen.
            2) Zum Finden der gewünschten Position könnte die Nutzung von count() über den betrachteten Achsenbereich helfen.

            Offenbar war z-index: +1; nur symbolisch gemeint, nun steht dort {$layer} statt +1. Sollte dann auch gleich so gesagt werden.

            Comment

            Working...
            X