2017-04-18 4 views
1

Ich habe Schwierigkeiten, meinen Kopf um die Syntax zu wickeln, die ich brauche, um ein Element zu erstellen, das ich möchte. Brandneu zu XML/XSLT und nicht sicher, ob dies der richtige Ansatz ist.Erstellen eines Elements basierend auf einer doppelten Bedingung mit XSLT

Ich versuche, meine XML-Datei in element-centric zu analysieren, so dass ich die Daten in einer lesbaren Struktur in einer Access-Datenbank formatieren kann.

Ich versuche, einen Datenwert aus einem Element basierend auf zwei Bedingungen zu ziehen.

Die Daten befinden sich in einem Element namens 'Reading' und die Daten sind mit 'value' gekennzeichnet. Über dem Element 'Reading' befindet sich das definierende Element 'ConsumptionSpec'.

Was ich zu testen versucht, ist das, was Maßeinheit (UOM) die aktuelle ‚ConsumptionSpec‘ eingeschaltet ist, und dann testen Sie ein anderes Attribut mit dem Namen ‚TouBucket‘, die entweder den Wert "hält TierA ',' TierB '/ C/D oder Total. Der UOM kann "kWh, kW, kVAh oder kVA" speichern. Ich versuche, den ersten auszulegen, während ich diesen Test wiederhole, um Elemente für jedes Tier (A bis D) und das Total zu machen. (Der Versuch, wie klar eine Erklärung zu geben, wie ich kann)

Derzeit Ich versuche xsl zu verwenden: foreach- die ConsumptionSpec rechts oben Lesen zu wählen, und dann xsl: wenn das zu testen UOM und TouBucket separat. Nach dem Test erstelle ich ein Element und versuche, den Wert des aktuellen Elements Reading zu ziehen.

Hier ist ein Auszug aus meinem XML, damit Sie sehen können, welche Werte ich während des Testens durchlaufen möchte.

<MeterReadings Irn="Null" Source="Remote" SourceName="Null" SourceIrn="Null" Initiator="Schedule" Purpose="Null" CollectionTime="2017-04-01 09:00:00" > 
    <Meter MeterIrn="Null" MeterName="Null" IsActive="true" SerialNumber="Null" MeterType="A3_ILN" Description="" InstallDate="2017-01-21 05:00:00" RemovalDate="" AccountIdent="Null" AccountName="" SdpIdent="" Location="Null" TimeZoneIndex="Null" Timezone="Null" TimeZoneOffset="300" ObservesDaylightSavings="false" MediaType="900 MHz" /> 

    <ReadingQualityIndicator Name="Tamper Alert" Value="true" /> 

    <ConsumptionData > 

     <ConsumptionSpec UOM="kWh" Direction="Delivered" TouBucket="Total" MeasurementPeriod="Current" Multiplier="1" /> 

     <Reading TimeStamp="2017-04-01 03:08:00" Value="902" /> 

    </ConsumptionData> 

    <ConsumptionData > 

     <ConsumptionSpec UOM="kWh" Direction="Delivered" TouBucket="TierA" MeasurementPeriod="Current" Multiplier="1" /> 

     <Reading TimeStamp="2017-04-01 03:08:00" Value="0" /> 

    </ConsumptionData> 

    <ConsumptionData > 

     <ConsumptionSpec UOM="kWh" Direction="Delivered" TouBucket="TierB" MeasurementPeriod="Current" Multiplier="1" /> 

     <Reading TimeStamp="2017-04-01 03:08:00" Value="0" /> 

    </ConsumptionData> 

    <ConsumptionData > 

     <ConsumptionSpec UOM="kWh" Direction="Delivered" TouBucket="TierC" MeasurementPeriod="Current" Multiplier="1" /> 

     <Reading TimeStamp="2017-04-01 03:08:00" Value="902" /> 

    </ConsumptionData> 

    <ConsumptionData > 

     <ConsumptionSpec UOM="kWh" Direction="Delivered" TouBucket="TierD" MeasurementPeriod="Current" Multiplier="1" /> 

     <Reading TimeStamp="2017-04-01 03:08:00" Value="0" /> 

    </ConsumptionData> 

Und hier ist meine aktuelle XSLT

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

     <!-- BY DEFAULT, elements and text nodes are copied, 
      and elements' attributes and contents are transformed as child nodes 
      of the output element --> 
     <xsl:template match="node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@* | node()"/> 
     </xsl:copy> 
     </xsl:template> 

     <!-- By default, attributes are transformed to elements --> 
     <xsl:template match="@*"> 
     <xsl:element name="{name()}"> 
      <xsl:value-of select="."/> 
     </xsl:element> 
     </xsl:template> 


     <!-- Certain elements have only their contents transformed --> 
     <xsl:template match=" 
      Meter | Status | ConsumptionData | 
      Statuses | MaxDemandData | MaxDemandSpec | 
      InstrumentationValue | IntervalData | IntervalSpec"> 
     <!-- no xsl:copy, and attribute children, if any, are ignored --> 
     <xsl:apply-templates select="@* | node()"/> 
     </xsl:template> 


     <!-- 
     Applies an extra element tag to the selected match 


     and pulls the value from the MeterReading ancestor it's 
     tagged under. 
     --> 

    <xsl:template match="Reading"> 

     <xsl:copy> 

      <xsl:element name="MeterReadingIRN"> 
       <xsl:value-of select="ancestor::MeterReadings/@Irn"/> 
      </xsl:element> 

       <!-- 
       Trying to get into the ConsumptionSpec tag it's related to, 
       then test what the unit of measurement is (UOM), 
       and then test what 'TouBucket' it is a part of (TierA/B/C/D or Total), 
       and THEN create a new element so that I can hold the 'value' that is inside 
       the Reading element, so that it will be referenced to that specific UOM. 
       --> 
      <xsl:for-each select="ancestor::ConsumptionSpec"> 
       <xsl:choose> 
        <xsl:when test="@UOM='kWh'"> 
         <xsl:when test="@TouBucket='Total'"> 

          <xsl:element name="kWhTotal"> 
           <xsl:value-of select="Reading/@Value"/> 
          </xsl:element> 

         </xsl:when> 
        </xsl:when> 
        <!-- Not sure how I can make my otherwise into a useful element here --> 
        <xsl:otherwise> 

         <xsl:element name="BlankTest"> 
          <xsl:value-of select="ancestor::MeterReadings/@Irn"/> 
         </xsl:element> 

        </xsl:otherwise> 
       </xsl:choose> 
      </xsl:for-each> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="Channel"> 
    <xsl:copy> 
    <xsl:element name="MeterReadingIRN"> 
    <xsl:value-of select="ancestor::MeterReadings/@Irn"/> 
    </xsl:element> 
    <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet> 

Sämtliche Vorschläge werden geschätzt, wurde mein Kopf gegen diese von Osterwochen hämmern! Lassen Sie es mich wissen, wenn es mehr Informationen gibt, die ich bereitstellen kann, um es verständlicher zu machen.

+0

Beiseite: in der Vorlage kommentiert mit "bestimmte Elemente haben nur ihre Inhalte umgewandelt", Sie sind in der Tat Umwandlung übereinstimmende Elemente Attribute, scheinbar im Gegensatz zu Ihrer Absicht. Sie transformieren auch Text-, Kommentar- und Verarbeitungskinder, nicht nur untergeordnete Elemente, falls sich das von dem unterscheidet, was Sie beabsichtigt haben. –

+0

In jedem Fall ist es nicht klar, welche Ausgabe Sie hoffen, entweder für diesen speziellen Fall oder für den allgemeineren, den Sie hoffen, schließlich zu handhaben. Versuchen Sie gerade, Elementnamen dynamisch aus der entsprechenden UOM und TouBucket zu erstellen? –

Antwort

1

Anstatt zu versuchen, Nest xsl:when Aussagen, die nicht erlaubt ist, die Syntax, die Sie wollen, ist dies ...

<xsl:when test="@UOM='kWh' and @TouBucket='Total'"> 

Sie haben jedoch ein Problem schon vor diesem Zeitpunkt wirklich, mit der Nutzung von xsl:for-each

<xsl:for-each select="ancestor::ConsumptionSpec"> 

Sie passen derzeit ein Reading Element an dieser Stelle, und so werden Sie zur Zeit auf diesem Element positioniert werden. ConsumptionSpec ist jedoch kein Vorgänger des aktuellen Elements Reading. Es ist ein Geschwister.

<ConsumptionData > 
    <ConsumptionSpec UOM="kWh" Direction="Delivered" TouBucket="TierA" MeasurementPeriod="Current" Multiplier="1" /> 
    <Reading TimeStamp="2017-04-01 03:08:00" Value="0" /> 
</ConsumptionData> 

Es ist ConsumptionData, dass sowohl die Eltern von ConsumptionSpec und Reading hier.

Beachten Sie auch, wenn <xsl:for-each select="ancestor::ConsumptionSpec"> ein Element ausgewählt hat, würde es Ihre Position von Reading zu ConsumptionSpec ändern.

Ich nehme an, Sie haben nur eine ConsumptionSpec pro Reading? In diesem Fall ist es möglicherweise besser, eine Variable zu verwenden.

diese Vorlage Spiel statt Versuche:

<xsl:template match="Reading"> 
    <xsl:copy> 
     <MeterReadingIRN> 
      <xsl:value-of select="ancestor::MeterReadings/@Irn"/> 
     </MeterReadingIRN> 
     <xsl:variable name="spec" select="../ConsumptionSpec" /> 
     <xsl:choose> 
      <xsl:when test="$spec/@UOM='kWh' and $spec/@TouBucket='Total'"> 
       <kWhTotal> 
       <xsl:value-of select="@Value"/> 
       </kWhTotal> 
       </xsl:when> 
       <xsl:otherwise> 
       <BlankTest> 
        <xsl:value-of select="ancestor::MeterReadings/@Irn"/> 
       </BlankTest> 
       </xsl:otherwise> 
      </xsl:choose> 
     <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 

Hinweis, verwenden xsl:element es keine wirkliche Notwendigkeit, ein neues Element in diesem Fall zu schaffen. Schreiben Sie einfach das Element, das Sie erstellen möchten.

Die Syntax .. im XPath bedeutet das übergeordnete wählen, so wird ../ConsumptionSpec die ConsumptionSpec wählen, die das Kind der gleichen Eltern wie Reading ist.

+0

Ahh, das ist ein Teil von dem, wo ich verwirrt wurde und versuchte, den ConsumptionSpec als Vorfahre zu bezeichnen, danke, dass du mir klargemacht hast, dass ich einfach losgelaufen bin von dem, was ich geschafft habe zu arbeiten.Werfen Sie einen Blick in Ihre Vorlage, vielen Dank für Ihre Eingabe. –

1

Es gibt gute Verwendungen für XSL-Iteration und bedingte Elemente, aber sie sind etwas ungewöhnlich. Wann immer Sie in Betracht ziehen, ein xsl:for-each, xsl:choose oder xsl:if Konstrukt zu verwenden, sollten Sie einen Moment innehalten, um darüber nachzudenken, ob das wirklich der beste Weg ist, um den Job zu erledigen.

Darüber hinaus ist es bei der Entwicklung eines Stylesheets nicht unbedingt vorteilhaft, Dinge in parallele Fälle aufzuteilen und sie einzeln zu verarbeiten. XSL ist keine prozedurale Programmiersprache und eignet sich nicht gut für diese Art von Denken. XSLT ist tendenziell besser durch eine Art ganzheitlichen Top-Down-Ansatz gedeckt, der an XML selbst erinnert.

Nehmen wir Ihre Vorlage für <Reading> Elemente als ein Beispiel, es scheint, als ob Sie sich vorbereiten, Regeln für eine Reihe von Einzelfällen zu schreiben, die alle ein gemeinsames Muster passen. Insbesondere scheint es, als ob Sie versuchen könnten, ein Element Kind der transformierten Elemente <Reading> dynamisch zu benennen, basierend auf den UOM und TouBucket der entsprechenden ConsumptionSpec. Diese ist, was <xsl:element> ist gut für; Sie brauchen es nicht wirklich für Ausgabeelemente, deren Namen festgelegt sind, da ein literales Ausgabeelement dafür geeignet ist. So könnten Sie alle die Fälle mit einer einfachen Vorlage wie diese umfassen:

<xsl:template match="Reading"> 
    <xsl:copy> 

    <!-- literal output element: --> 
    <MeterReadingIRN> 
     <xsl:value-of select="ancestor::MeterReadings/@Irn"/> 
    </MeterReadingIRN> 

    <xsl:element name="{concat(../ConsumptionSpec/@UOM, ../ConsumptionSpec/@TouBucket)}"> 
     <xsl:value-of select="@Value"/> 
    </xsl:element> 

    <xsl:apply-templates select="@*|node()"/> 
    </xsl:copy> 
</xsl:template> 

Auch wenn Sie mehr Unterschiede zwischen den Transformationen für Reading Elemente müssen als die bereitstellt, sollten Sie strukturieren Ihre Sheet betrachten, um zu Verwenden Sie spezifischere match Ausdrücke auf Ihren Vorlagen, genauer select Ausdrücke, um Knoten auszuwählen, auf die Vorlagen angewendet werden sollen, und/oder verschiedene Vorlagenmodi, um die Umwandlungen auszudrücken und zu steuern.

+0

Entschuldigung, ich wurde gestern aus dem Büro herausgerufen und bin nicht darauf zurück gekommen, schaue es jetzt an und verstehe es viel besser, danke John. –

Verwandte Themen