2009-04-30 7 views
4

Ich versuche, eine Transformation für ein XML-Dokument durchzuführen. Meine XML kann auf zwei verschiedene Arten von Basiselement je nach dem Wert eines bestimmten Elements Transformation führen:Leere xmlns = "" Attribute von Import

<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 oder StructureB werden dann mit ihren eigenen Namensraum und schemaLocations erstellt:

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

StructureA & B teilen einige gemeinsame Elemente, so dass diese in einer separaten Datei namens "xmlcommon.xslt" definiert sind, in die beide Strukturen Vorlagen enthalten. In dieser XML-Datei ist kein Standard-Namespace definiert, da ich möchte, dass er aus dem in StrukturA oder StrukturB definierten Namespace verwendet werden kann. Aber wenn ich meine Transformation ausführen, wird alle Vorlagen gezogen aus dem gemeinsamen Datei Ergebnis in leeren xmlns Attribute:

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

Wenn die Validierung, die leere Namespace dann eine anstelle den richtig Eltern verwendet wird. Weiß jemand, wie ich meine Vorlagen in meiner allgemeinen Datei davon abhalten kann, diese leeren xmlns-Attribute hinzuzufügen?

Hier ist ein Ausschnitt aus der gemeinsamen Datei:

<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> 

Antwort

9

Der Schlüssel ist daran zu erkennen ist, dass Ihr Sheet den Namen jedes Elements diktiert Sie in den Ergebnisbaum hinzuzufügen. Der Name eines Elements besteht aus zwei Teilen: dem lokalen Namen und dem Namespace-URI. In Ihrem obigen Code geben Sie den lokalen Namen (den Wert $xmlElem) an, aber Sie geben keinen Namespace-URI an, was bedeutet, dass standardmäßig der leere String verwendet wird. (Tatsächlich nimmt es den Standard-Namespace dieses Stylesheet-Moduls an; da es keinen gibt, ist es der leere String.) Mit anderen Worten, das Element wird nicht in einem Namespace sein. Bei der Serialisierung des Dokuments muss der XSLT-Prozessor die Unerklärungen xmlns="" enthalten, um den oben angezeigten Standardnamespace zu deklarieren. Andernfalls würde das Element diesen Namespace übernehmen, was nicht von Ihrem Stylesheet diktiert wurde. Die am wenigsten aufdringliche Art, dies zu beheben, wäre, einen weiteren Parameter hinzuzufügen (z. B. $namespaceURI), genau wie bei $xmlElem. Dann würden Sie schreiben:

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

Nun nimmt das resultierende Element auf, was Namespace Sie sagen, es zu übernehmen (was den Effekt der Beseitigung dieser Standard-Namespace un-Erklärungen haben wird).

Das sollte Ihre Frage beantworten. Ich biete folgendes als kostenloses Bonusmaterial an. ;-)

Sie sollten den text() Nodentest in Ihrem Wertvergleich entfernen. Sehr selten müssen Sie die Werte von Textknoten direkt vergleichen. Stattdessen können Sie einfach den String-Wert des Elements selbst vergleichen (der als Verkettung der String-Werte aller nachfolgenden Textknoten definiert ist). Das würde wie folgt aussehen:

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

Der Vorteil es auf diese Weise tun, ist, dass der Code nicht brechen, wenn es ein Kommentar ist da drin versteckt:

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

In diesem Fall gibt es zwei Textknoten ("2" und "00"). Ihr ursprünglicher Test würde fehlschlagen, da geprüft wird, ob einer von ihnen gleich "200" ist.In diesem Fall ist es nicht sehr wahrscheinlich, aber in jedem Fall ist es eine gute Übung, den String-Wert des Elements zu testen (im Gegensatz zu seinen Textknoten-Children), wenn dies Ihre Absicht ist.

Schließlich ermutige ich Sie, über Vorlagenregeln und XPath-Kontext zu lernen. Ich tendiere dazu, <xsl:choose>, <xsl:call-template> und <xsl:with-param> wann immer möglich zu vermeiden. Zum einen können Vorlagenregeln Ihnen helfen, viele der hässlichen, ausführlichen Teile von XSLT zu vermeiden.

<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> 

Auch wenn Sie halten mit <xsl:call-template>, sollten Sie nicht, dass $structure Parameter übergeben müssen, da der aktuelle Knoten wird in der aufgerufenen Vorlage unverändert bleiben. Sie können auf //databean (oder /databean, die ich vermute, was Sie meinen) genauso einfach von innerhalb Ihrer StructureA oder StructureB Vorlagen, weil der aktuelle Knoten immer noch "/" (der Dokument-Knoten).

Wenn Sie mehr über XSLT Kernverarbeitungsmodell interessiert sind, und seine mächtigsten Funktionen (Template-Regeln), dann ermutige ich Sie "How XSLT Works", die freie Probe Kapitel aus meinem XSLT 1.0 Pocket Reference auszuchecken.

Ich hoffe, dies war hilfreich für Sie, auch wenn es mehr ist, als Sie erwartet haben!

+0

Gute Erklärung, Evan. Nichts mehr hinzuzufügen. +1 von mir. Willkommen bei SO. –

+0

Danke Evan. Das ist eine große Hilfe :) Und danke für die zusätzlichen Informationen auch. Ich berühre nicht oft XSLTs, daher ist es schön, die besseren Möglichkeiten zu kennen. –