如何使用XSL 1.0解析父标记的特定第一个子<dateline>标记数据,并跳过输出

时间:2016-06-16 23:56:58

标签: xslt xslt-1.0

从下面的示例输入和相应的输出中,我需要一个XSL转换,只跳过<dateline>父标记下<body>字段的第一次出现。

       <!--Given sample Input XML: -->
            <content>
               <data>
                <datatext>
                     <message name="message">
                        <p>Test message paragraph. 
                           <dateline name="dateline">Message datelines</dateline>? 
                           <annotation type="note">Test message Note.</annotation>
                        </p>
                     </message>
                     <head name="head">
                        <p>Test Head paragraph <annotation type="note">Head notes </annotation> paragraph.
                            <dateline name="dateline">Head dateline</dateline>
                        </p>
                     </head>
                     <body name="body">
                        <p>
                           Test first Body paragraph.
                           <annotation type="note">First Body notes.</annotation>
                        </p>
                        <p>Test Second Body paragraph.</p>
                        <p>
                           <annotation type="note">Second Body notes.</annotation>
                           Test third Body paragraph.
                           <dateline name="dateline">SECOND DATELINE</dateline>
                        </p>
                        <p>Test Fouth Body paragraph.</p>
                        <p>
                           <dateline name="dateline">THIRD DATELINE</dateline> 
                           Test fourth Body paragraph.
                           <annotation type="note">Third Body notes.</annotation>
                        </p>
                     </body>
                  </datatext>
               </data>
            </content>

应删除预期输出,<dateline>标记的第一次出现,

        <!-- Expected Output XML -->
        <content>
           <data>
            <datatext>
                 <message name="message">
                    <p>Test message paragraph. 
                       <dateline name="dateline">Message datelines</dateline>? 
                       <annotation type="note">Test message Note.</annotation>
                    </p>
                 </message>
                 <head name="head">
                    <p>Test Head paragraph <annotation type="note">Head notes </annotation> paragraph.
                        <dateline name="dateline">Head dateline</dateline>
                    </p>
                 </head>
                 <body name="body">
                    <p>
                       Test first Body paragraph.
                       <annotation type="note">First Body notes.</annotation>
                    </p>
                    <p>Test Second Body paragraph.</p>
                    <p>
                       <annotation type="note">Second Body notes.</annotation>
                       Test third Body paragraph.
                    </p>
                    <p>Test Fouth Body paragraph.</p>
                    <p>
                       <dateline name="dateline">THIRD DATELINE</dateline> 
                       Test fourth Body paragraph.
                       <annotation type="note">Third Body notes.</annotation>
                    </p>
                 </body>
              </datatext>
           </data>
        </content>

2 个答案:

答案 0 :(得分:1)

  

仅跳过第一次出现的<dateline>字段   <body>父标记

首先,bodydateline祖先,而不是

现在,由于您要复制除一个节点以外的所有内容,因此最好从身份转换模板(复制所有内容)开始作为规则,并为相关节点添加例外:

XSLT 1.0

<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:strip-space elements="*"/>

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

<xsl:template match="body//dateline[generate-id()=generate-id(ancestor::body/descendant::dateline[1])]"/>

</xsl:stylesheet> 

为什么必须这么复杂:

为了选择 dateline的第一个body后代,您必须使用以下表达式:

body/descendant::dateline[1]

而不是:

body//dateline[1]

XPath specification

中对此进行了解释
  

注意:位置路径//para[1] 与位置路径/descendant::para[1]的含义相同。后者选择第一个   后代para元素;前者选择所有后代para   这些元素是父母的第一个para子女。

然而,表达式:

body/descendant::dateline[1]

不是有效的匹配模式。虽然模式可以使用//运算符,但它们不能使用后代轴:https://www.w3.org/TR/xslt/#patterns

因此,我选择匹配dateline的后代的任何 body,并添加一个谓词,用于比较唯一ID当前dateline与真正是祖先descendant的第一个body的{​​{1}}。这是有效的,因为谓词中允许后代轴

答案 1 :(得分:0)

这是一个可能的解决方案。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:variable name="bdl" select="//body//dateline"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="dateline[index-of($bdl,.) = 1]"/>
</xsl:stylesheet>

起初我以为你可以只用

<xsl:template match="//body//dateline[1]"/>

但是这不起作用,因为[1]谓词依赖于焦点和上下文,并且正文中的两个dateline标记首先位于其直接父级之下。此解决方案首先构建所有正文dateline标记的序列(在$bdl中),然后仅删除与列表中第一个条目匹配的标记。

可能有更好的&#34;或更多惯用的方法来实现这一点,我希望其中一位XSLT大师也会回答。