2010-04-24 7 views
8

Ho un foglio xslt piuttosto complicato che trasforma un formato xml in un altro utilizzando i modelli. Tuttavia, nel risultante xml, ho bisogno di escludere tutti gli elementi vuoti. Come è fatto?XSLT: Come escludere elementi vuoti dal mio risultato?

è così che l'XSLT di base si presenta come:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:far="http://www.itella.com/fargo/fargogate/" xmlns:a="http://tempuri.org/XMLSchema.xsd" xmlns:p="http://tempuri.org/XMLSchema.xsd"> 
    <xsl:import href="TransportCDMtoFDM_V0.6.xsl"/> 
    <xsl:import href="ConsignmentCDMtoFDM_V0.6.xsl"/> 
    <xsl:template match="/"> 
     <InboundFargoMessage> 
      <EdiSender> 
       <xsl:value-of select="TransportInformationMessage/SenderId"/> 
      </EdiSender> 
      <EdiReceiver> 
       <xsl:value-of select="TransportInformationMessage/RecipientId"/> 
      </EdiReceiver> 
      <EdiSource> 
       <xsl:value-of select="TransportInformationMessage/Waybill/Parties/Consignor/Id"/> 
      </EdiSource> 
      <EdiDestination>FARGO</EdiDestination> 
      <Transportations> 
       <xsl:for-each select="TransportInformationMessage/TransportUnits/TransportUnit"> 
        <xsl:call-template name="transport"/> 
       </xsl:for-each> 
       <xsl:for-each select="TransportInformationMessage/Waybill/TransportUnits/TransportUnit"> 
        <xsl:call-template name="transport"/> 
       </xsl:for-each> 
       <xsl:for-each select="TransportInformationMessage/Waybill"> 
        <EdiImportTransportationDTO> 
         <Consignments> 
          <xsl:for-each select="Shipments/Shipment"> 
           <xsl:call-template name="consignment"/> 
          </xsl:for-each> 
         </Consignments> 
         <EdiTerminalDepartureTime> 
          <xsl:value-of select="DatesAndTimes/EstimatedDepartureDateTime"/> 
          <xsl:value-of select="DatesAndTimes/DepartureDateTime"/> 
         </EdiTerminalDepartureTime> 
         <EdiAgentTerminalArrivalDate> 
          <xsl:value-of select="DatesAndTimes/EstimatedArrivalDateTime"/> 
          <xsl:value-of select="DatesAndTimes/ArrivalDateTime"/> 
         </EdiAgentTerminalArrivalDate> 
         <EdiActivevehicle> 
          <xsl:value-of select="Vehicle/TransportShiftNumber"/> 
         </EdiActivevehicle> 
         <EdiConveyerZipCodeTown><xsl:text> </xsl:text></EdiConveyerZipCodeTown> 
        </EdiImportTransportationDTO> 
       </xsl:for-each> 
      </Transportations> 
     </InboundFargoMessage> 
    </xsl:template> 
</xsl:stylesheet> 

ciò che deve essere aggiunto, in modo che gli elementi vuoti sono lasciati fuori?

Ad esempio, un frammento della XML risultante:

<?xml version="1.0" encoding="UTF-8"?> 
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd" 
     xmlns:far="http://www.itella.com/fargo/fargogate/" 
     xmlns:a="http://tempuri.org/XMLSchema.xsd"> 
    <EdiSender>XXXX</EdiSender> 
    <EdiReceiver>YYYY</EdiReceiver> 
    <EdiSource>TR/BAL/IST</EdiSource> 
    <EdiDestination>FARGO</EdiDestination> 
    <Transportations> 
     <EdiImportTransportationDTO> 
      <Consignments> 
       <EdiImportConsignmentDTO> 
        <ConsignmentLines> 
         <EdiImportConsignmentLineDTO> 
          <DangerousGoodsItems> 
           <EdiImportDangerGoodsItemDTO> 
            <EdiKolliTypeOuter/> 
            <EdiKolliTypeInner/> 
            <EdiTechnicalDescription/> 
            <EdiUNno/> 
            <EdiClass/> 
            <EdiDangerFactor/> 
            <EdiEmergencyTemperature/> 
           </EdiImportDangerGoodsItemDTO> 
          </DangerousGoodsItems> 
          <BarCodes> 
           <EdiImportConsignmentLineBarcodeDTO/> 
          </BarCodes> 
          <EdiNumberOfPieces>00000002</EdiNumberOfPieces> 
          <EdiGrossWeight>0.000</EdiGrossWeight> 
          <EdiHeight/> 
          <EdiWidth/> 
          <EdiLength/> 
          <EdiGoodsDescription/> 
          <EdiMarkingAndNumber/> 
          <EdiKolliType>road</EdiKolliType> 
          <EdiCbm/> 
          <EdiLdm/> 
         </EdiImportConsignmentLineDTO> 

che ha davvero bisogno di essere:

<?xml version="1.0" encoding="UTF-8"?> 
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd" 
     xmlns:far="http://www.itella.com/fargo/fargogate/" 
     xmlns:a="http://tempuri.org/XMLSchema.xsd"> 
    <EdiSender>XXXX</EdiSender> 
    <EdiReceiver>YYYY</EdiReceiver> 
    <EdiSource>TR/BAL/IST</EdiSource> 
    <EdiDestination>FARGO</EdiDestination> 
    <Transportations> 
     <EdiImportTransportationDTO> 
      <Consignments> 
       <EdiImportConsignmentDTO> 
        <ConsignmentLines> 
         <EdiImportConsignmentLineDTO> 
          <DangerousGoodsItems/> 
          <BarCodes/> 
          <EdiNumberOfPieces>00000002</EdiNumberOfPieces> 
          <EdiGrossWeight>0.000</EdiGrossWeight> 
          <EdiKolliType>road</EdiKolliType> 
         </EdiImportConsignmentLineDTO> 

In altre parole: Elementi vuoti dovrebbero essere lasciati fuori.

+0

Si prega di essere più specifico. Vuoi saltare i nodi vuoti nei cicli for-each? Vuoi saltare elementi in cui il valore di è vuoto? – harpo

+0

Voglio escludere elementi in cui il valore è vuoto. Modificherò la domanda per aggiungere un esempio ... –

+0

Buona domanda (+1). Vedi la mia risposta per una soluzione che è probabilmente la soluzione XSLT più semplice e fondamentale. :) –

risposta

11

Il codice XSLT (parziale) fornito illustra bene un antipattern XSLT. Prova quasi sempre a evitare l'uso di <xsl:for-each>.

Di seguito è riportato un documento XML di esempio e una trasformazione che copia tutti i nodi ad eccezione degli elementi "vuoti". Qui con "vuoto" intendiamo o senza figli, o con un solo nodo figlio bianco.

documento XML:

<a> 
<b> 
    <c> </c> 
    <d/> 
    <e>1</e> 
</b> 
</a> 

Trasformazione:

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

<xsl:strip-space elements="*"/> 

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

<xsl:template match= 
    "*[not(node())] 
    | 
    *[not(node()[2]) 
    and 
    node()/self::text() 
    and 
    not(normalize-space()) 
    ] 
    "/> 
</xsl:stylesheet> 

Risultato:

<a> 
    <b> 
     <e>1</e> 
    </b> 
</a> 

Do atto:

  1. L'uso della Regola Identità.

  2. Come eseguire l'override della regola di identità con un modello che corrisponde solo agli elementi "vuoti". Poiché questo modello non fa nulla (non ha alcun corpo), questo non copia ("elimina") gli elementi "vuoti".

Utilizzando e l'abbandono del principio di identità è il più importante modello di progettazione XSLT.

+2

Penso che preferirei' * [non (*) e non (normalize-space())] '. Sì, gli elementi contenenti solo commenti e/o le istruzioni di elaborazione verranno filtrate da questo, ma immagino che probabilmente non è indesiderabile. Detto questo, il tuo commento circa l'originale essendo un antipattern XSLT è perfetto. –

+0

Un collega mi ha passato una soluzione simile che ora sembra funzionare, quindi contrassegnerò questa risposta come * la * soluzione. –

+0

@ Robert-Rossney: Preferirei non lavorare in modo eccessivo per ipotesi - questo è ciò che SO è per: gli utenti possono fare domande nuove, più specifiche, non aggiustando la loro domanda originale di tempo. Grazie per il tuo apprezzamento. –

1

Questo è probabilmente il modo più semplice:

<xsl:for-each select="Nodes/Node[text() != '']"> 

</xsl:for-each> 

Se si ha il controllo della generazione XML allora non aggiungere il nodo principale se non ci sono bambini. Indipendentemente dal modo in cui scegli XSL è abbastanza dettagliato.

+0

Ma non dovrei aggiungere questo a ciascun nodo? Non sarebbe affatto semplice, perché ci sono alcune centinaia di nodi in totale! –

+0

@Fedor - XSL non è stato progettato pensando alla precisione. :( – ChaosPandion

+0

@Fedor - Guardando il tuo aggiornamento questo potrebbe soddisfare le tue esigenze – ChaosPandion

0

Ci sono alcuni casi complicati in cui la risposta di Dimitre (che è certamente l'approccio giusto) potrebbe comportarsi inaspettatamente.Per esempio, se hai riscritta tua XSLT per utilizzare il modello di identità (che si dovrebbe), e si è creato un modello come questo:

<xsl:template match="Vehicle/TransportShiftNumber[. != '123']"> 
    <EdiActivevehicle> 
     <xsl:value-of select="."/> 
    </EdiActivevehicle> 
</xsl:template> 

la trasformazione può comunque creare vuoti EdiActivevehicle elementi se TransportShiftNumber è vuota.

In genere, se più modelli corrispondono a un nodo, verrà selezionato quello più specifico. "Più specifico" in genere significa che i pattern che hanno un predicato batteranno modelli che non lo fanno. (Le regole effettive di risoluzione dei conflitti sono più coinvolgenti, vedere la sezione 5.5 del suggerimento XSLT.) In questo caso, sia il template sopra che il template degli elementi vuoti usano predicati, e quindi entrambi hanno la stessa priorità.

Così il processore XSLT farà una delle due cose: che segnalerà un errore (che è permesso, anche se non ho mai visto un processore XSLT che ostile), o sarà selezionare il modello che appare ultima nel foglio di stile.

Ci sono due modi per risolvere questo problema. O mettere il modello di elemento vuoto-filtro nella parte inferiore del foglio di stile, o esplicitamente assegnare una priorità che è maggiore di 0,5 (che è il valore di default per la maggior parte dei modelli che hanno predicati):

I' Probabilmente faccio il secondo, perché generalmente strutturo i fogli di stile con l'aspettativa che l'ordine dei modelli non sia significativo e non voglio sorprese sgradevoli se comincio a spostare le cose. Ma sicuramente inserisco un commento là dentro spiegando me stesso: non ho mai visto nessuno effettivamente usare una priorità esplicita su un modello.

0

Ho iniziato con la soluzione di Dimitre sopra (grazie!), Ma ho ancora avuto elementi di uscita o nulli con bambini nulli in questo modo:

    <a> 
        <b> 
         <c/> 
         <d/>   
        </b>    
       </a> 

Questo sembra funzionare ... ancora test.

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:date="http://exslt.org/dates-and-times" 
    xmlns:exsl="http://exslt.org/common" 
    xmlns:func="http://exslt.org/common" 
    xmlns:random="http://exslt.org/random" 
    xmlns:regexp="http://exslt.org/regular-expressions" 
    xmlns:set="http://exslt.org/sets" 
    xmlns:str="http://exslt.org/strings" 
    version="1.0" 
    extension-element-prefixes="date exsl func random regexp set str"> 

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

    <xsl:strip-space elements="*"/> 

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

    <xsl:template match= 
    "*[not(node())] 
    | 
    *[not(string())] 
    "/> 
</xsl:stylesheet> 
Problemi correlati