Domanda

Sto tentando di fare una trasformazione su un documento XML. La mia trasformazione XML può comportare due diversi tipi di elementi base a seconda del valore di un determinato elemento:

<xsl:template match="/">
  <xsl:choose>
    <xsl:when test="/databean/data[@id='pkhfeed']/value/text()='200'">
      <xsl:call-template name="StructureA">
        <xsl:with-param name="structure" select="//databean" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="StructureB">
        <xsl:with-param name="structure" select="//databean" />
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

StructureA o StructureB vengono quindi creati con i propri spazi dei nomi e schemiLocations:

<StructureA xmlns="http://...">

Struttura A e amp; B condivide alcuni elementi comuni, quindi questi sono definiti in un file separato chiamato " xmlcommon.xslt " che entrambe le strutture includono modelli da. Questo file xmlcommon non ha uno spazio dei nomi predefinito definito in quanto voglio che sia utilizzabile dallo spazio dei nomi definito in StructureA o StructureB. Ma quando eseguo la mia trasformazione, tutti i modelli estratti dal file comune producono attributi xmlns vuoti:

<StructureA xmlns="http://...">
  <SharedElement xmlns="">Something</SharedElement>
</StructureA>

Durante la convalida, viene quindi utilizzato lo spazio dei nomi vuoto anziché quello genitore corretto. Qualcuno sa come posso impedire ai miei modelli nel mio file comune di aggiungere quegli attributi xmlns vuoti?

Ecco un frammento del file comune:

<xsl:stylesheet version="1.0" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template name="ControlledListStructure">
    <xsl:param name="xmlElem" />
    <xsl:param name="structure" />

    <xsl:element name="{$xmlElem}">
      <!-- Blah blah blah -->
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>
È stato utile?

Soluzione

La cosa fondamentale da capire è che il tuo foglio di stile detta il nome di ogni elemento che aggiungi all'albero dei risultati. Il nome di un elemento ha due parti: il nome locale e l'URI dello spazio dei nomi. Nel codice sopra, fornisci il nome locale (il valore di $ xmlElem ), ma non specifichi un URI dello spazio dei nomi, il che significa che per impostazione predefinita sarà la stringa vuota. (In realtà, assume lo spazio dei nomi predefinito di quel modulo del foglio di stile; dato che non ce ne sono, allora è la stringa vuota.) In altre parole, l'elemento sarà non in uno spazio dei nomi . Durante la serializzazione del documento, il processore XSLT deve includere le dichiarazioni non dichiarate xmlns = " " in modo da annullare lo spazio dei nomi predefinito visualizzato in alto. Altrimenti, l'elemento assumerebbe quello spazio dei nomi, che non è quello che il tuo foglio di stile ha dettato. Il modo meno invadente per risolvere questo problema sarebbe aggiungere un altro parametro (ad es. $ namespaceURI ), proprio come hai fatto con $ xmlElem . Quindi scrivi:

<xsl:element name="{$xmlElem}" namespace="{$namespaceURI}">

Ora, l'elemento risultante assumerà qualunque spazio dei nomi tu gli dica di assumere (il che avrà l'effetto di rimuovere quelle non dichiarazioni dello spazio dei nomi predefinito).

Questo dovrebbe rispondere alla tua domanda. Offro quanto segue come materiale bonus gratuito. ; -)

Dovresti rimuovere il test del nodo text () nel confronto dei valori. Molto raramente dovrai confrontare direttamente i valori dei nodi di testo. Invece, puoi semplicemente confrontare il valore stringa dell'elemento stesso (che è definito come la concatenazione dei valori stringa di tutti i suoi nodi di testo discendente). Sarebbe così:

<xsl:when test="/databean/data[@id='pkhfeed']/value = '200'">

Il vantaggio di farlo in questo modo è che il tuo codice non si romperà se c'è un commento nascosto lì dentro:

<value>2<!--test-->00</value>

In questo caso, ci sono due nodi di testo ("2" e "00"). Il tuo test originale fallirebbe, poiché controlla se qualcuno di loro è uguale a "200". Non è molto probabile che accada in questo caso, ma in ogni caso testare il valore di stringa dell'elemento (al contrario dei suoi figli del nodo di testo) è una buona pratica quando questa è la tua intenzione.

Infine, ti incoraggio a conoscere le regole del modello e il contesto XPath. Tendo a evitare < xsl: scegli > , < xsl: call-template > e < xsl: with-param > quando possibile. Per prima cosa, le regole del modello possono aiutarti a evitare molte parti brutte e dettagliate di XSLT.

<xsl:template match="/databean[data[@id='pkhfeed']/value = '200']" priority="1">
  <StructureA xmlns="http://...">
    ...
  </StructureA>
</xsl:template>

<xsl:template match="/databean">
  <StructureB xmlns="http://...">
    ...
  </StructureB>
</xsl:template>

Anche se continui a usare < xsl: call-template > , non dovresti passare quel parametro $ struttura , poiché il nodo corrente rimarrà invariato nel modello chiamato. Puoi accedere a // databean (o / databean , che sospetto sia ciò che intendi) con la stessa facilità da uno dei tuoi StructureA o StructureB , poiché il nodo corrente sarà comunque " / " (il nodo del documento).

Se sei interessato a saperne di più sul modello di elaborazione di base di XSLT e sulle sue funzionalità più potenti (regole del modello), ti incoraggio a dare un'occhiata a " Come funziona XSLT " , il capitolo di esempio gratuito dal mio Riferimento tascabile XSLT 1.0 .

Spero che questo ti sia stato utile, anche se è più di quanto ti aspettassi!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top