XSLT 2.0 - 按lang / locale层次结构和内容过滤节点

时间:2014-11-13 17:02:29

标签: xml xslt xpath xslt-2.0

使用XSLT 2.0,如何根据下面的规则/要求动态处理XML文档以删除具有xml:lang属性的节点?

要求:

  • 使用属性xml:lang
  • 查找任何节点(及其相同类型的直接兄弟姐妹)
  • 知道xml:lang值具有基于语言/区域设置的3层层次结构,下面是非详尽的示例:
    1. x-default(第1层,最高)
    2. en(第2层,语言前缀,其他值示例:fr,es,ru)
    3. en-US(第3层,语言前缀后跟后缀,其他值示例:en-GB,en-CA)
  • 根据已知的层次结构,应删除重复的值。
  • 删除重复项时,还要考虑兄弟姐妹可能拥有的其他属性。
  • 保留XML文档的其余部分不受干扰

示例数据集:

<?xml version="1.0" encoding="UTF-8"?>
<arbitrarydepth>
<scenario1 xml:lang="x-default">A Default Node Value</scenario1>
<scenario1 xml:lang="en">A Default Node Value</scenario1>
<scenario1 xml:lang="en-US">A Default Node Value</scenario1>

<scenario2 xml:lang="x-default">The orig value</scenario2>
<scenario2 xml:lang="en">The orig value</scenario2>
<scenario2 xml:lang="en-US">A new value</scenario2>

<scenario3 xml:lang="x-default">The orig value</scenario3>
<scenario3 xml:lang="en">A new value</scenario3>
<scenario3 xml:lang="en-US">The orig value</scenario3>

<scenario4 xml:lang="x-default">The orig value</scenario4>
<scenario4 xml:lang="en">An english value</scenario4>
<scenario4 xml:lang="en-US">An english US value</scenario4>
<scenario4 xml:lang="fr">A french value</scenario4>
<scenario4 xml:lang="fr-FR">A french value</scenario4>
<scenario4 xml:lang="fr-CA">A french Canada value</scenario4>

<scenario5 xml:lang="x-default" attr0="something here">The orig value</scenario5>
<scenario5 xml:lang="en" attr1="Some attribute">The orig value</scenario5>
<scenario5 xml:lang="en-US" attr2="some other attribute">The orig value</scenario5>
<scenario5 xml:lang="fr" attr0="something here">The orig value</scenario5>
<scenario5 xml:lang="fr-FR">The orig value</scenario5>
</arbitrarydepth>

示例结果集:

<?xml version="1.0" encoding="UTF-8"?>
<arbitrarydepth>
<scenario1 xml:lang="x-default">A Default Node Value</scenario1>

<scenario2 xml:lang="x-default">The orig value</scenario2>
<scenario2 xml:lang="en-US">A new value</scenario2>

<scenario3 xml:lang="x-default">The orig value</scenario3>
<scenario3 xml:lang="en">A new value</scenario3>
<scenario3 xml:lang="en-US">The orig value</scenario3>

<scenario4 xml:lang="x-default">The orig value</scenario4>
<scenario4 xml:lang="en">An english value</scenario4>
<scenario4 xml:lang="en-US">An english US value</scenario4>
<scenario4 xml:lang="fr">A french value</scenario4>
<scenario4 xml:lang="fr-CA">A french Canada value</scenario4>

<scenario5 xml:lang="x-default" attr0="something here">The orig value</scenario5>
<scenario5 xml:lang="en" attr1="Some attribute">The orig value</scenario5>
<scenario5 xml:lang="en-US" attr2="some other attribute">The orig value</scenario5>
</arbitrarydepth>

1 个答案:

答案 0 :(得分:1)

这应该满足所有要求,除了最后一个关于匹配动态属性的要求:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="*">
    <xsl:variable name="elementName" select="name()"/>
    <xsl:variable name="contentText" select="normalize-space(.)"/>
    <xsl:choose>
        <xsl:when test="not(@xml:lang)">
            <!-- Non-lang element -->
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:apply-templates select="*"/>
            </xsl:copy>
        </xsl:when>
        <xsl:when test="@xml:lang='x-default'">
            <!-- Tier 1: xml:lang="x-default" -->
            <xsl:copy-of select="."/>
        </xsl:when>
        <xsl:when test="contains(@xml:lang,'-')">
            <!-- Tier 3: xml:lang="en-US" -->
            <xsl:variable name="baselang" select="substring-before(@xml:lang, '-')"/>
            <xsl:choose>
                <xsl:when test="../*[name()=$elementName][@xml:lang=$baselang][normalize-space(.)=$contentText]">
                    <!-- Same text as Tier 2 parent -->
                </xsl:when>
                <xsl:when test="../*[name()=$elementName][@xml:lang=$baselang]">
                    <xsl:copy-of select="."/>
                </xsl:when>
                <xsl:when test="../*[name()=$elementName][@xml:lang='x-default'][normalize-space(.)=$contentText]">
                    <!-- Same text as Tier 1 parent -->
                </xsl:when>
                <xsl:when test="../*[name()=$elementName][@xml:lang='x-default']">
                    <xsl:copy-of select="."/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <!-- Tier 2: xml:lang="en" -->
            <xsl:choose>
                <xsl:when test="../*[name()=$elementName][@xml:lang='x-default'][normalize-space(.)=$contentText]">
                    <!-- Same text as Tier 1 parent -->
                </xsl:when>
                <xsl:when test="../*[name()=$elementName][@xml:lang='x-default']">
                    <xsl:copy-of select="."/>
                </xsl:when>
                <xsl:otherwise>
                    <!-- No matching parent -->
                    <xsl:copy-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
</xsl:stylesheet>

演示:http://www.xsltcake.com/slices/uopn40

匹配父级和子级之间的动态属性实际上非常复杂。您必须循环使用属性并与当前父级进行比较。如果父项上缺少任何属性,或者它的值不同,则必须保留新元素。

为了满足最后的要求,我认为你必须转向命令式语言(C#,JavaScript,Java)。