5 Stimmen

Emulation einer C-Aufzählung in XSLT mit optionalen Werten

Ich versuche, eine XSLT-Konvertierung, die C-Code generiert, die folgende XML umgewandelt werden soll zu machen:

<enum name="anenum">
  <enumValue name="a"/>
  <enumValue name="b"/>
  <enumValue name="c" data="10"/>
  <enumValue name="d" />
  <enumValue name="e" />
</enum>

Es sollte in C-Code wie folgt umgewandelt werden:

enum anenum {
   a = 0,
   b = 1,
   c = 10,
   d = 11,
   e = 12
}

oder alternativ (da der C-Präprozessor die Summierung übernimmt):

   enum anenum {
       a = 0,
       b = 1,
       c = 10,
       d = c+1,
       e = c+2
    }

Der Kern meiner XSLT sieht so aus:

<xsl:for-each select="enumValue">
  <xsl:value-of select="name"/>
  <xsl:text> = </xsl:text>
  <xsl:choose>
    <xsl:when test="string-length(@data)&gt;0">
      <xsl:value-of select="@data"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="position()-1"/>
    </xsl:otherwise>
  </xsl:choose>
  <xsl:text>,

(der Einfachheit halber lasse ich einen Teil des Codes "kein Komma am letzten Element" weg)

Dieses Beispiel erzeugt nicht die richtigen Werte für d y e

Ich habe versucht, es für die Variable d y e aber bis jetzt bin ich nicht erfolgreich.

Mit Konstruktionen wie:

<xsl:when test="string-length(preceding-sibling::enumValue[1]/@datavalue)&gt;0">
    <xsl:value-of select="preceding-sibling::enumValue/@data + 1"/>
</xsl:when>

...funktionieren nur für den ersten Wert nach dem angegebenen Wert (in diesem Fall d ).

Wer kann mir helfen? Wahrscheinlich denke ich zu sehr verfahrenstechnisch...

4voto

Dimitre Novatchev Punkte 234995

Eine nicht rekursive Lösung, die Schlüssel verwendet :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:strip-space elements="*"/>
 <xsl:output method="text"/>

 <xsl:key name="koffsetEnums" match="enumValue[@data]"
  use="generate-id()"/>

    <xsl:template match="enum">
      enum <xsl:value-of select="@name"/> {
      <xsl:apply-templates select="enumValue"/>
      }
    </xsl:template>

    <xsl:template match="enumValue">
      <xsl:value-of select="concat(@name, ' = ')"/>

      <xsl:variable name="voffsetValueId" select=
       "generate-id((. | preceding-sibling::enumValue)
                                            [@data][last()]
                  )"/>

      <xsl:choose>
        <xsl:when test="not($voffsetValueId)">
          <xsl:value-of select="concat(position(),'&#xA;      ')"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="vinitOffset" select=
           "key('koffsetEnums', $voffsetValueId)/@data"
           />

           <xsl:value-of select=
            "$vinitOffset
            +
               count(preceding-sibling::enumValue)
             -
               count(key('koffsetEnums', $voffsetValueId)/preceding-sibling::enumValue)
            "
            />
           <xsl:text>&#xA;      </xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

Wenn die obige Transformation auf das ursprünglich bereitgestellte XML-Dokument angewendet wird :

<enum name="anenum">
    <enumValue name="a"/>
    <enumValue name="b"/>
    <enumValue name="c" data="10"/>
    <enumValue name="d" />
    <enumValue name="e" />
</enum>

das gewünschte Ergebnis erzielt wird :

enum anenum {
      a = 1
      b = 2
      c = 10
      d = 11
      e = 12

      }

2voto

Dimitre Novatchev Punkte 234995

Eine bessere Lösung mit Schlüsseln, die den größten Teil der Nutzung der Vorgänger-Geschwister-Achse vermeidet :

Diese Umwandlung :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:output method="text"/>
<!--                                              -->   
    <xsl:key name="ksimpleEnValues" match="enumValue[not(@data)]"
     use="generate-id(preceding-sibling::enumValue[@data][1])"/>
<!--                                              -->       
    <xsl:template match="enum">
    enum <xsl:value-of select="@name"/>
         {      
          <xsl:apply-templates select=
           "key('ksimpleEnValues', '')
           "/>
           <xsl:apply-templates select="enumValue[@data]"/>
         }
    </xsl:template>
<!--                                              -->
    <xsl:template match="enumValue">
      <xsl:param name="pOffset" select="0"/>
        <xsl:value-of select=
         "concat(@name, ' = ', position()+$pOffset,'&#xA;      ')"/>
    </xsl:template>
<!--                                              -->
    <xsl:template match="enumValue[@data]">
        <xsl:value-of select=
         "concat(@name, ' = ', @data,'&#xA;      ')"/>
<!--                                              -->
      <xsl:apply-templates select=
           "key('ksimpleEnValues', generate-id())">
       <xsl:with-param name="pOffset" select="@data"/>
      </xsl:apply-templates>
  </xsl:template>       
</xsl:stylesheet>

bei Anwendung auf das ursprünglich bereitgestellte XML-Dokument :

<enum name="anenum">
    <enumValue name="a"/>
    <enumValue name="b"/>
    <enumValue name="c" data="10"/>
    <enumValue name="d" />
    <enumValue name="e" />
</enum>

Erzielt das gewünschte Ergebnis :

enum anenum
 {      
  a = 1
  b = 2
  c = 10
  d = 11
  e = 12
 }

Erläuterung :

  1. Der Schlüssel namens ksimpleEnValues indiziert alle enumValue Elemente, die nicht über das data Attribut. Die Indizierung erfolgt über den generate-id()-Wert des ersten vorangehenden enumValue Element, das eine data Attribut.

  2. So key('ksimpleEnValues', someId) ist das Nodeset, das alle enumValue Elemente nach dem enumValue die ihre generate-id() gleich someId und all diese enumValue Elemente sind vor dem nächsten enumValue mit einer data Attribut, sofern ein solches vorhanden ist.

  3. key('ksimpleEnValues', '') ist die Knotenmenge aller enumValue Elemente, die keinen vorangehenden enumValue Element mit einer data Attribut.

  4. Die Vorlage, die mit enumValue nimmt einen optionalen Parameter $pOffset in dem der Wert des data Attribut aus dem unmittelbar vorangehenden enumValue Element mit diesem Attribut übergeben wird, andernfalls wird der Standardwert für $pOffset ist 0.

  5. Die passende Vorlage enumValue Elemente, die eine data Attribut erzeugt seinen Enum-Wert (@name = @data) und wendet dann Vorlagen auf alle enumValue Elemente zwischen sich und dem nächsten (falls vorhanden) enumValue mit einer data Attribut. Der Wert des Attributs data Attribut wird als das $pOffset und wird zur relativen Position jedes ausgewählten enumValue Element, wenn es die Ergebnisse seiner Verarbeitung produziert.

1voto

Goran Punkte 6712

Sie können "Variablen" in xsl nicht ändern, aber Sie können Rekursion verwenden. Verwenden Sie keine Prädikate mit vorangehenden Geschwistern, es sei denn, es ist absolut notwendig, da sie Ihre Leistung beeinträchtigen.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:template match="/" >
        <xsl:call-template name="printEnum">
            <xsl:with-param name="value" select="0"/>
            <xsl:with-param name="position" select="1"/>
        </xsl:call-template>
    </xsl:template>

    <xsl:template name="printEnum">
        <xsl:param name="position"/>    
        <xsl:param name="value" select="0"/>
        <xsl:variable name="node" select="/enum/enumValue[$position]"/>
        <xsl:variable name="enumValue">
            <xsl:choose>
                <xsl:when test="$node/@data">
                    <xsl:value-of select="$node/@data"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$value + 1"/>
                </xsl:otherwise>
            </xsl:choose>       
        </xsl:variable>     
        <xsl:value-of select="concat($node/@name, ' = ', $enumValue, ' , ')"/>
        <xsl:if test="/enum/enumValue[$position + 1]">
            <xsl:call-template name="printEnum">
                <xsl:with-param name="value" select="$enumValue"/>
                <xsl:with-param name="position" select="$position + 1"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X