XSLT为什么这个非常基本的选择不起作用?

时间:2011-06-20 17:17:26

标签: xml xslt select

我正在尝试从AP Webfeed中消化内容,但出于某种原因应该是一个非常简单的for-each循环让我适合。 Feed是来自AP的xml utf-8,使用php xsltprocessor和simplexml。

问题是我无法定位我希望循环的正确节点。 Feed本身是根元素,它具有feed的一些属性,然后是几个“entry”子文章。其中每一个都有条目的子项属性(如版权),然后是实际的nitf含量(铅和身体)

似乎我应该能够<xsl:for-each select="feed/entry" />但是如果我试图通过名称引用'feed'或'entry',我什么也得不到。我甚至不能做<xsl:value-of select="feed/id" /> - 奇怪的是我可以得到// @ nitf @ version正确返回,但无法通过feed / entry / content / nitf / @ version获得它

我可以通过<xsl:for-each select="//nitf"&gt;来处理一些内容获取文章正文或nitf节点的任何后代但不是更高的元素(如//条目)。我可以获得更接近root的内容的唯一方法是嵌套<xsl:for-each="/*" />从根(feed)开始并向下钻取 - 这似乎是错误的。

如果有人能指出我正确的方向,我真的很感激它,令我感到沮丧的是,看起来如此简单的事情让我暂时停留了一段时间。

格式为:

<feed>
   <id></id>
   <published></published>
   <entry>
      <copyright></copyright>
      <content>
         <nitf>
            <head></head>
            <body></body>
         </nitf>
      </content>
   </entry>
   <entry>
      <content>
         <nitf>
            <head></head>
            <body></body>
         </nitf>
      </content>
   </entry>
</feed>



<?xml version="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:template match="/">
      <!-- this does loop through nitf -->
      <xsl:for-each select="descendant::*/nitf">
         <nitf_title></nitf_title>
      </xsl:for-each>

      <!-- I want to loop on these instead but this never loops -->
      <xsl:for-each select="descendant::*/entry">
         <entry_title><entry_title>
      </xsl:for-each>
   </xsl:template>
</xsl:stylesheet>

抱歉,我试图保持简短,所以我嘲笑了源码,下面的实际例子

<?xml version="1.0" encoding="utf-8" ?>

<feed xmlns="http://www.w3.org/2005/Atom" xmlns:apcm="http://ap.org/schemas/03/2005/apcm" xmlns:apnm="http://ap.org/schemas/03/2005/apnm" xmlns:georss="http://www.georss.org/georss">

  <id>urn:publicid:ap.org:31998</id>    
  <title type="xhtml">    
    <apxh:div xmlns:apxh="http://www.w3.org/1999/xhtml">    
      <apxh:span>AP Online National News</apxh:span>    
    </apxh:div>    
  </title>    
  <apcm:Property Name="FeedProperties">    
    <apcm:Property Name="Entitlement" Id="urn:publicid:ap.org:product:31998" Value="AP Online National News" />    
    <apcm:Property Name="FeedSequencing">
      <apcm:Property Name="sequenceNumber" Id="111835329" />
      <apcm:Property Name="minDateTime" Value="2011-06-20T16:56:08.047Z" />    
    </apcm:Property>    
  </apcm:Property>    
  <updated>2011-06-20T16:56:08.047Z</updated>    
  <author>    
    <name>The Associated Press</name>    
    <uri>http://www.ap.org</uri>    
  </author>    
  <rights></rights>    
  <link rel="self" href="http://syndication.ap.org" />    
<entry xmlns="http://www.w3.org/2005/Atom">
  <id>urn:publicid:ap.org:badf779c9d5246b5acb21430ed2214fb</id>
  <title>APFN-US--Gas Drilling-Chemicals</title>
  <updated>2011-06-20T16:56:08.047Z</updated>
  <published>2011-06-20T16:25:39Z</published>
  <author>
    <name>AP</name>
  </author>
  <rights>Copyright 2011</rights>
  <content type="text/xml">
    <nitf version="-//IPTC//DTD NITF 3.4//EN" change.date="October 18, 2006" change.time="19:30" xmlns="">
      <head>
        <docdata>
          <doc-id regsrc="AP" />
          <date.issue norm="20110620T162539Z" />
          <ed-msg info="Eds: APNewsNow." />
          <doc.rights owner="http://www.ap.org" agent="http://license.icopyright.net" type="none" />
          <doc.copyright holder="AP" year="2011" />
        </docdata>
      </head>
      <body>
        <body.head>
          <hedline>
            <hl1 id="headline">Texas becomes 1st to require fracking disclosure</hl1>
            <hl2 id="originalHeadline">Texas becomes 1st to require fracking disclosure</hl2>
          </hedline>
          <distributor>The Associated Press</distributor>
          <dateline>
            <location>HOUSTON</location>
          </dateline>
        </body.head>
        <body.content>
          <block id="Main">
              <p>HOUSTON (AP) — Texas </p>
            </block>
        </body.content>
        <body.end />
      </body>
    </nitf>
  </content>
  <apcm:ContentMetadata xmlns:apcm="http://ap.org/schemas/03/2005/apcm">
    <apcm:DateLineLocation City="Houston" Country="USA" CountryArea="TX" CountryAreaName="Texas" CountryName="United States" />
    <apcm:Priority Numeric="4" Legacy="r" />
    <apcm:ConsumerReady>TRUE</apcm:ConsumerReady>
    <apcm:DateLine>HOUSTON</apcm:DateLine>
  </apcm:ContentMetadata>
</entry>

<entry xmlns="http://www.w3.org/2005/Atom">
  <id>urn:publicid:ap.org:57582781c3a841a2b9849231a4abdb63</id>
  <title>US--Medicare-Prevention</title>
  <updated>2011-06-20T16:54:57.963Z</updated>
  <published>2011-06-20T16:54:43Z</published>
...

2 个答案:

答案 0 :(得分:2)

正如上面的评论中所指出的,您的问题与源文档中的命名空间有关。例如,您尝试匹配名为“entry”的元素,但实际元素的限定名称为{http://www.w3.org/2005/Atom}:entry。

您应该使用前缀重写xpath以包含命名空间限定符,然后将该前缀映射到适当的值。结果,“entry”变为“atom:entry”,一些封闭元素的原子声明为xmlns:atom =“http://www.w3.org/2005/Atom”。

答案 1 :(得分:2)

与上面的注释不同,您实际上不需要任何名称空间声明来选择nitf元素,作为无名称空间(xmlns="")中的元素。实际上,您只需使用nitf即可在文档中选择任何//元素。例如:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
       <xsl:copy-of select="//nitf"/>
    </xsl:template>

</xsl:stylesheet>

将输出类型为nitf的所有节点的所有节点,无论父节点派生的名称空间如何。

不同的命名空间改为entry,即xmlns="http://www.w3.org/2005/Atom"。要正确选择此元素,您必须在文档中声明名称空间前缀,并相应地使用它。例如:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:atom="http://www.w3.org/2005/Atom">

    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:for-each select="//nitf">
            <!-- iterate on nitf children -->
        </xsl:for-each>

        <xsl:for-each select="//atom:entry">
            <!-- iterate on entry children -->
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

首先会对所有{}:nitf的子项进行迭代,然后对任何{http://www.w3.org/2005/Atom}:entry的子进行迭代。


为什么您的第一个XPath无法正常工作

在第一个XPath中:

descendant::*/nitf

您正在使用轴来选择任何nitf。使用*强制XPath选择所有nitf元素, null 命名空间中元素的后代。但是,您没有null命名空间的nitf元素后代。 nitfhttp://www.w3.org/2005/Atom命名空间uri中的元素的子元素。

这里使用轴的正确方法是,在声明了http://www.w3.org/2005/Atom的名称空间前缀之后(如前面的示例所示):

descendant::atom:*/nitf

或者,您也可以使用低级别:

descendant::node()/nitf

最后,最简单的方法是(如上面的第一个例子所示):

//nitf

请注意,最后两个XPath将选择任何名称空间中限定的元素后代的nitf个元素。因此,当您完全了解输入文档并且知道自己在做什么时,应该使用它们。