2010-12-03 52 views
1

以微弱的嘗試澄清編輯。系統通過XSL處理器將XML轉換爲另一個XML。XSL:是否有使用謂詞變量的快速替代方法?

我正在爲有插卡的機器做一些XSL規則的東西。這些卡片將自己的XSL規則提供給主要主機,然後將這些規則集成到一個超級XSL文件中,以便自己處理,並且還將這些文件發送到HTTP'd到機器上的Web瀏覽器。瀏覽器用於設置機器的配置項目,XSL用於更改和/或隱藏和/或取消隱藏某些XML。爲了簡化我的查詢,我發明了一個小例子。

當用戶將項目配置爲B類型(可選擇幾個如A,B,C,...)時,接下來的兩個項目也將被更改(以某種方式)。在真實交易中,有一個屬性「隱藏」設置爲true或false,並且還設置了子元素。真實情況中的下兩個項目將被隱藏,並且子元素也會被更改。

當用戶將項目從類型B更改爲類型A時,我需要確定哪些其他節點需要將其「隱藏」屬性設置爲false。用戶會根據他們認爲合適的方式更改子元素。

所有這些都是「循環」,並且是有序的,所以如果一個節點被設置爲B類型,並且它是最後一個節點(沒有下一個兄弟節點),那麼受影響的節點是該集合中的第一個節點。 (在XPath術語中,如果節點[4]是類型B,則節點[1]和[2]將被隱藏並更改)。

所以,在這裏我的例子,我有作爲輸入XML:

<topline> 
    <midline type="A" name="mid1" hidden="false"><source name="off"/></midline> 
    <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline> 
    <midline type="A" name="mid3" hidden="false"><source name="off"/></midline> 
    <midline type="A" name="mid4" hidden="false"><source name="off"/></midline> 
</topline> 

和XSL將其更改爲:

<topline> 
    <midline type="A" name="mid1" hidden="false"><source name="off"/></midline> 
    <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline> 
    <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline> 
    <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline> 
</topline> 

現在,如果用戶改變了主意,並改變mid2成爲A型:

<topline> 
    <midline type="A" name="mid1" hidden="false"><source name="off"/></midline> 
    <midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline> 
    <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline> 
    <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline> 
</topline> 

那麼XSL將取消隱藏mid2的兄弟姐妹之後的通告,所以結果壽ld是:

<topline> 
    <midline type="A" name="mid1" hidden="false"><source name="off"/></midline> 
    <midline type="A" name="mid2" hidden="false"><source name="input 1"/></midline> 
    <midline type="A" name="mid3" hidden="false"><source name="input 1"/></midline> 
    <midline type="A" name="mid4" hidden="false"><source name="input 1"/></midline> 
</topline> 

這是我正在努力的第二步。我所做的解決這個問題對我來說是相當難看的,但也許是不可避免的,因爲我試圖實現的並不是真正的XSL友好。

我已經登陸了做:

<xsl:for-each select="topline"> 
    <xsl:for-each select="midline"> 
    <xsl:variable name="masterPosition" select="position()"/> 
    <xsl:choose> 
     <xsl:when test="@type='B'> 
     hide the next two nodes. This is easy: 
      translate($masterPosition, '1234', '2341') 
     works nicely. 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:variable name="prior1" select="translate(masterPosition, '1234', '4123')"/> 
      <xsl:variable name="test1" select="../midline[$prior1]/source[1]/@name='off'"/> 
      this second line doesn't work: I only get the first node, always. 
      So instead I have 
      <xsl:variable name="test2"> 
       <xsl:choice> 
        <xsl:when test="$masterPosition='1'"><xsl:value-of select="../midline[4]/source"/></xsl:when> 
        and so on for the other masterPositions 
       </xsl:choice> 
      </xsl:variable> 
      and this is repeated for a few other variables, one each for each relevant 
      prior position and for the fields I need to change. I then use these 
      variables to change the XML - there's something in the main machine's 
      processing to enable this, I believe it is non-standard, so please ignore: 
      (At least it doesn't run against Xalan). 
      <set key={$test2}/@hidden, value="false")/>    
     </xsl:otherwise> 
    </xsl:choose> 
    <xsl:for-each> 
</xsl:for-each> 

是否有這樣做,你能想到的更優雅的方式?如果沒有,不要擔心,我相信我的黑客會起作用,也不應該消耗太多的MIPS。

我無法使用<xsl:key>,因爲我們的系統無法應對:我們有多個XSL源代碼被合成爲一個,並且代謝腳本(無法控制)根本不理解<xsl:key>,所以如果有一個使用鍵的解決方案,我無法使用它。

感謝 理查德

+0

您需要確保將所有標記正確標記爲降價編輯器中的代碼。看起來在「我想做什麼」中有一些標籤丟失,至少我沒有得到你想要達到的。 – Lucero 2010-12-03 10:54:49

+0

這很奇怪。格式不是很好,但(對我來說,但後來我寫了它)似乎足夠清晰:比較第一個XML和第二個:源名稱更改爲「mid1」和「mid4」,因爲它們以循環方式,「mid2」。我需要一種方法來告訴節點「mid4」,確定它是否與「mid2」有關,因爲當處理「mid2」時,它改變了「mid4」。如果是這樣的話,我就把它放在一邊,否則我還有其他的事情需要我去做(「這是mid4作爲我的例子)。我希望這有助於,這聽起來有點漫不經心:-) – Richard 2010-12-03 11:01:42

+0

好問題,+1。請參閱我的答案,以獲得從B型到A型逆轉的完整解決方案。 :) – 2010-12-03 14:45:09

回答

1

下面就做你想做的一個方式,無需按鍵,基於模板匹配:

<!-- start with the identity transform --> 
<xsl:template match="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*" /> 
    </xsl:copy> 
</xsl:template> 

<!-- <midline>s that have two preceding siblings --> 
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) &gt; 1]"> 
    <xsl:apply-templates select="." mode="source"> 
    <xsl:with-param name="circular" select=" 
     preceding-sibling::midline[position() &lt;= 2] 
    " /> 
    </xsl:apply-templates> 
</xsl:template> 

<!-- <midline>s that have one preceding sibling --> 
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 1]"> 
    <xsl:apply-templates select="." mode="source"> 
    <xsl:with-param name="circular" select=" 
     (preceding-sibling::midline|../midline[last()]) 
    " /> 
    </xsl:apply-templates> 
</xsl:template> 

<!-- <midline>s that have no preceding sibling --> 
<xsl:template match="midline[@type='A'][count(preceding-sibling::midline) = 0]"> 
    <xsl:apply-templates select="." mode="source"> 
    <xsl:with-param name="circular" select=" 
     ../midline[position() &gt; last() - 2] 
    " /> 
    </xsl:apply-templates> 
</xsl:template> 

<!-- this template creates a new node with the wanted source --> 
<xsl:template match="midline" mode="source"> 
    <xsl:param name="circular" select="." /> 
    <xsl:variable name="prevB" select="$circular[@type = 'B' and not(@mode = off)]" /> 
    <xsl:copy> 
    <xsl:copy-of select="@*" /> 
    <xsl:attribute name="hidden"> 
     <xsl:value-of select="boolean($prevB)" /> 
    </xsl:attribute> 
    <xsl:choose> 
     <xsl:when test="$prevB"> 
     <xsl:copy-of select="$prevB/source" /> 
     </xsl:when> 
     <xsl:otherwise> 
     <xsl:copy-of select="source" /> 
     </xsl:otherwise> 
    </xsl:choose> 
    </xsl:copy> 
</xsl:template> 

爲您的樣品輸入,輸出將是:

<topline> 
    <midline type="A" name="mid1" hidden="false"><source name="off"/></midline> 
    <midline type="B" name="mid2" hidden="false"><source name="input 1"/></midline> 
    <midline type="A" name="mid3" hidden="true"><source name="input 1"/></midline> 
    <midline type="A" name="mid4" hidden="true"><source name="input 1"/></midline> 
</topline> 

請注意,您的描述與您的示例輸出相矛盾 - 這是您嚴格遵循描述時所得到的結果,unles我誤解了你。

<xsl:with-param>用於傳遞「循環前同胞」(或節點集爲@type=B'節點)的兩個節點。

模板<xsl:template match="midline" mode="source">使用該參數:如果它在那裏,它檢查您的條件(即其中一個是@type='B'而不是@mode='off')。

如果存在這樣的節點,它將複製它的<source>,否則它會複製原始的<source>

1

要做到逆變換,你要想這

當該變換是在以下(提供)XML其中用戶已經剛剛改變type='B'type='A'施加:

<topline> 
    <midline type="A" name="mid1" hidden="false"> 
     <source name="off"/> 
    </midline> 
    <midline type="A" name="mid2" hidden="false"> 
     <source name="input 1"/> 
    </midline> 
    <midline type="A" name="mid3" hidden="true"> 
     <source name="input 1"/> 
    </midline> 
    <midline type="A" name="mid4" hidden="true"> 
     <source name="input 1"/> 
    </midline> 
</topline> 

的蒼白泰德,產生正確的結果hidden="false"被設定爲前B型接下來的兩個圓形兄弟姐妹):

<topline> 
    <midline type="A" name="mid1" hidden="false"> 
     <source name="off"/> 
    </midline> 
    <midline type="A" name="mid2" hidden="false"> 
     <source name="input 1"/> 
    </midline> 
    <midline type="A" name="mid3" hidden="false"> 
     <source name="input 1"/> 
    </midline> 
    <midline type="A" name="mid4" hidden="false"> 
     <source name="input 1"/> 
    </midline> 
</topline> 

與此源XML文件(真實圓形兄弟姐妹):

<topline> 
    <midline type="A" name="mid1" hidden="true"> 
     <source name="off"/> 
    </midline> 
    <midline type="A" name="mid2" hidden="false"> 
     <source name="input 1"/> 
    </midline> 
    <midline type="A" name="mid3" hidden="false"> 
     <source name="input 1"/> 
    </midline> 
    <midline type="A" name="mid4" hidden="true"> 
     <source name="input 1"/> 
    </midline> 
</topline> 

再次通緝,正確的結果產生

請注意:在使用XPath mod運營商的確定圓形的兄弟姐妹位置。