這是我已經能夠拿出這麼迄今爲止最好的:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" version="1.0"/>
<xsl:template match="/root">
<xsl:copy>
<xsl:for-each select="*">
<xsl:call-template name="check-if-allowed">
<xsl:with-param name="path" select="local-name(.)"/>
</xsl:call-template>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template name="check-if-allowed">
<xsl:param name="path"/>
<xsl:copy>
<xsl:if test="$path = document('filter.xml')//RequiredElements/elementName/text()">
<xsl:attribute name="flagged-by-filter">true</xsl:attribute>
</xsl:if>
<xsl:choose>
<xsl:when test="*">
<xsl:for-each select="*">
<xsl:call-template name="check-if-allowed">
<xsl:with-param name="path" select="concat($path, '/', local-name(.))"/>
</xsl:call-template>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="text()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
讓我們過的是:第一個模板您/root
元素相匹配。它將製作一個淺度副本,然後爲每個子元素調用模板check-if-allowed
,並將該子元素的本地名稱作爲參數path
傳入。
check-if-allowed
模板接受名稱爲path
的參數。它會對其節點進行淺度複製,然後測試參數是否包含在從文檔中進行的選擇中。這必須是第二個文檔的路徑,其中包含允許的路徑列表。如果測試成功(即如果path
參數在中顯示爲elementName
的文本內容),那麼它也將添加名稱爲flagged-by-filter
且值爲true
的屬性。
之後,xsl:choose
是要做的兩件事之一。如果當前的元素有子元素,它將調用相同的check-if-allowed
模板,但每次使用path
參數時,將添加該元素的本地名稱。如果沒有子元素,只需要複製當前元素中可能存在的任何文本。
請注意,這是一個非常不完整的解決方案。它忽略了屬性,並且不適用於混合內容(即與元素混合的文本)。這裏
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:copy><xsl:apply-templates select="*"/></xsl:copy>
</xsl:template>
<xsl:template match="*[//*[@flagged-by-filter='true']]">
<xsl:copy><xsl:apply-templates select="*"/></xsl:copy>
</xsl:template>
<xsl:template match="*[* and not(//*[@flagged-by-filter='true']) and @flagged-by-filter='true']">
<xsl:copy></xsl:copy>
</xsl:template>
<xsl:template match="*[not(*) and @flagged-by-filter='true']">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="*[not(*) and not(@flagged-by-filter='true')]"/>
</xsl:stylesheet>
同樣,非常基本的工作:
這第二個樣式可以應用到第一個這樣做的實際濾波的結果。它不處理屬性,並且由於某種原因,總是會傳遞elem4
,所以仍然存在問題。不知道爲什麼,調試器顯示它總是匹配第二個模板,但我無法想象如何。
這是更常用的聲明式XSLT風格。第一個模板匹配根。它只是複製它,然後將模板應用到其子元素。第二個模板匹配具有flagged-by-filter="true"
屬性的後代的任何元素。第三個模板匹配任何至少有一個子元素的元素,具有標記的過濾器屬性但沒有具有所述屬性的後代。第四個模板匹配任何沒有子元素但本身被標記的元素。最終模板匹配任何沒有子元素的元素,也不會被標記。
雖然這不是您的問題的完整解決方案,但我希望這足以讓您走上正軌。如果您無權自由地應用兩個連續的XSLT轉換,則需要根據需要找到從第一個XSLT應用內容的方法。我想不出如何做到這一點,但也許別人有一個好主意。
說了這麼一句話,對於像這樣的問題,要麼不使用XSLT,要麼只是基於過濾器XML以編程方式生成樣式表。以上在性能方面將會非常糟糕,因爲我們一直在將其他文檔應用XPath表達式。不僅如此,每次都必須完全解析。我曾經有過類似的設置,發現性能很差。所以我將對第二個文檔的訪問權限改爲一個擴展函數,該函數將使用預加載的數據調用Java方法。
XSLT對於某些東西來說非常棒,但是當你陷入這樣的複雜情況時,我認爲它最好與其他語言結合使用。
@_G_H:在您的回答結束時,您意味着每次調用document()函數都會導致(重新)解析XML文檔。這是不正確的,事實上,如果一個給定的XSLT處理器重新解析,它是不符合規範的 - 因爲隨着W3C XSLT規範定義它,document()函數必須是* stable * - 即每次使用相同的參數調用時都會生成完全相同的節點。 –
@DimitreNovatchev有趣......我已經觀察到使用'document()'調光性能,但。儘管在將第二個文檔拖放到評估XPath表達式的過程中時,可能會出現這種情況。這是因爲第二個文檔基本上是鍵值對,這意味着搜索屬性值而不是元素名稱。不是一個很好的方法。我將它改爲將屬性文檔讀入「Properties」對象,並通過擴展函數簡單地訪問它。性能提高了不少。 –
@_G_H:如果鍵值對文檔不小,則通過它進行篩選必然會導致性能瓶頸。這裏的正確解決方案是使用''和'key()'函數 - 如果執行多個搜索,這會顯着提高性能。 –