如何确定XML节点是否具有“不具有特定属性”的最后一个祖先?

时间:2012-08-09 21:55:51

标签: xml xslt xpath-1.0


  • <p>标记中的任何 <body>标记都应转换为Body_Text

    < / LI>
  • 具有最后一个祖先<p> 而不属性“<sec>”的sec-type代码应转换为Flush_Text(这会覆盖第一个Body_Text转换。)

  • 具有最后一个祖先<p> 属性“<sec sec-type="irrelevant-attribute-name>”)的sec-type代码应转换为{{1} }。




Body_Text应转换为<sec><p>asdf</p></sec>

<sec><Flush_Text>asdf</Flush_Text></sec>应为<sec sec-type="whatevs"><p>asdf</p></sec>


同样,任何进一步嵌套到具有此<sec sec-type="whatevs"><Body_Text>asdf</Body_Text></sec>属性的祖先应该仍为sec-type

Body_Text应为<sec sec-type="whatevs"><sec><p>asdf</p></sec></sec>




这是我的XML:

<sec sec-type="whatevs"><sec><Body_Text>asdf</Body_Text></sec>


...这是我的XSL, 正常工作:

<root>
  <body>
  <sec sec-type="asdf">
    <title>This is an H1</title>

    <sec>
      <title>This is an H2</title>

      <sec>
        <title>This is an H3</title>
        <p>This SHOULD be "Body_Text", but it's "Flush_Text"</p>
      </sec> <!-- end of H3 -->
    </sec> <!-- end of H2 -->
  </sec> <!-- end of H1 -->

  <sec>
    <p>This is Flush_Text</p>
  </sec>
    <p>This is Body_Text</p>
  </body>
</root>


......这是不正确的输出:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>

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

        <!-- Body_Text -->
        <xsl:template match="body//p">
            <Body_Text>
                <xsl:apply-templates select="@*|node()"/>
            </Body_Text>
        </xsl:template>

        <!-- Flush_Text -->
        <xsl:template match="sec//p">
          <xsl:if test="not(@sec-type)">
            <Flush_Text>
                <xsl:apply-templates select="@*|node()"/>
            </Flush_Text>
          </xsl:if>
        </xsl:template>

        <!-- H1 -->
        <xsl:template match="sec//title">
            <H1>
                <xsl:apply-templates select="@*|node()"/>
            </H1>
        </xsl:template>

        <!-- H2 -->
        <xsl:template match="sec//sec//title">
            <H2>
                <xsl:apply-templates select="@*|node()"/>
            </H2>
        </xsl:template>

        <!-- H3 -->
        <xsl:template match="sec//sec//sec//title">
            <H3>
                <xsl:apply-templates select="@*|node()"/>
            </H3>
        </xsl:template>
</xsl:stylesheet>

请注意,此示例中的<?xml version="1.0" encoding="utf-16"?> <root> <body> <sec sec-type="asdf"> <H1>This is an H1</H1> <sec> <H2>This is an H2</H2> <sec> <H3>This is an H3</H3> <Flush_Text>This SHOULD be "Body_Text", but it's "Flush_Text"</Flush_Text> </sec> <!-- end of H3 --> </sec> <!-- end of H2 --> </sec> <!-- end of H1 --> <sec> <Flush_Text>This is Flush_Text</Flush_Text> </sec> <Body_Text>This is Body_Text</Body_Text> </body> </root> 的第一个实例应转换为<p>,但它将转换为Body_Text

2 个答案:

答案 0 :(得分:1)

这是一个解决方案,可以实现我想要的想法。很难解释你的问题。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes" method="xml" />
  <xsl:strip-space elements="*" />

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

  <xsl:template match="title">
    <xsl:element name="H{count(ancestor::sec) + 1}">
      <xsl:apply-templates select="node() | @*" />
    </xsl:element>
  </xsl:template>

  <xsl:template match="p[ancestor::sec[last()][@sec-type]]">
    <Body_Text>
      <xsl:apply-templates select="node() | @*" />
    </Body_Text>
  </xsl:template>

  <xsl:template match="p">
    <Flush_Text>
      <xsl:apply-templates select="node() | @*" />
    </Flush_Text>
  </xsl:template>

</xsl:stylesheet>

http://www.xmlplayground.com/vmuroB

答案 1 :(得分:0)

好的,为了在这里产生想要的结果,我已将语句<xsl:template match="sec//p">(在Flush_Text下的XSL中)更改为<xsl:template match="p[ancestor::sec[last()][not(@sec-type)]]">,并且还删除了if语句。

这是更正的XSL:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>

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

        <!-- Body_Text -->
        <xsl:template match="body//p">
            <Body_Text>
                <xsl:apply-templates select="@*|node()"/>
            </Body_Text>
        </xsl:template>

    <!-- Flush_Text -->
    <xsl:template match="p[ancestor::sec[last()][not(@sec-type)]]">
        <Flush_Text>
            <xsl:apply-templates select="@*|node()"/>
        </Flush_Text>
    </xsl:template>

        <!-- H1 -->
        <xsl:template match="sec//title">
            <H1>
                <xsl:apply-templates select="@*|node()"/>
            </H1>
        </xsl:template>

        <!-- H2 -->
        <xsl:template match="sec//sec//title">
            <H2>
                <xsl:apply-templates select="@*|node()"/>
            </H2>
        </xsl:template>

        <!-- H3 -->
        <xsl:template match="sec//sec//sec//title">
            <H3>
                <xsl:apply-templates select="@*|node()"/>
            </H3>
        </xsl:template>
</xsl:stylesheet>

...产生所需的输出:

<root>
<body>
<sec sec-type="asdf">
<H1>This is an H1</H1>
<sec>
<H2>This is an H2</H2>
<sec>
<H3>This is an H3</H3>
<Body_Text>This SHOULD be "Body_Text", but it's "Flush_Text"</Body_Text>
</sec>

</sec>

</sec>

<sec>
<Flush_Text>This is Flush_Text</Flush_Text>
</sec>
<Body_Text>This is Body_Text</Body_Text>
</body>
</root>


这是在http://xslt.online-toolz.com/tools/xslt-transformation.php测试的。

感谢@Tomalak指导我使用ancestor xpath轴的正确方向。

在这里,我匹配了 的任何<sec>中的最后一个祖先(我错误地称之为“最高父级”)<p>属性sec-type,并将其转换为Flush_Text。这阻止了此示例中<p>的第一个实例,其<sec sec-type...作为其“最后一个祖先”,而不是Flush_Text并允许Body_Text覆盖。

另外,我喜欢Tomalak使用H1-H3的自动化...我还在试验这个,并且在我完全理解它之前不想使用它;)