XSLT 模板不适用于所有使用 lxml 的元素

XSLT template does not apply to all elements using lxml

我对 XSLT templates 和 when/how 的使用感到困惑。假设我有以下 XML 文件:

<book>
  <chapter> 1 </chapter>
  <chapter> 2 </chapter>
</book>

我想按顺序匹配所有章节。这是一个 XSLT 样式表:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  <xsl:template match="book">                                                     
    <h1>book</h1>                                                               
  </xsl:template>                                                                 
  <xsl:template match="chapter">                                                  
    <h2>chapter <xsl:value-of select="."/></h2>                                   
  </xsl:template>                                                                                                                                                 
</xsl:stylesheet>                                                               

样式表的结果是

<h1>book</h1>

没有预期的章节编号。添加一个 <xsl:apply-templates /> at the end of the book matching template didn't help. I'd like to do without an xls:for-each 不过。

编辑 我应该提到这一点:我正在使用 Python 的 lxml module which uses libxml2 and libxslt。以下代码 不会 产生预期的结果,而是产生上面的结果:

import lxml.etree
xml = lxml.etree.XML("""                                                    
    <book>                                                                          
      <chapter> 1 </chapter>                                                        
      <chapter> 2 </chapter>                                                        
    </book>                                                                         
""")                                                                            
transform = lxml.etree.XSLT( lxml.etree.XML("""                                  
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
      <xsl:template match="book">                                                   
        <h1>book</h1>                                                               
        <xsl:apply-templates  />                                                    
      </xsl:template>                                                               
      <xsl:template match="chapter">                                                
        <h2>chapter <xsl:value-of select="."/></h2>                                 
      </xsl:template>                                                               
    </xsl:stylesheet>                                                               
""") )                                                                                                                                                      
html = transform(xml)                                                            
print( lxml.etree.tostring(html, pretty_print=True) )

奇怪的是,正确的(预期的)结果被证明 here. Accessing libxslt directly through the Python bindings 而不是通过 lxml 工作,但是:

import libxml2                                                                  
import libxslt  

doc = libxml2.parseDoc("""                                                  
<book>                                                                      
  <chapter> 1 </chapter>                                                    
  <chapter> 2 </chapter>                                                    
</book>                                                                     
""")                                                                        

styledoc = libxml2.parseDoc("""                                             
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  <xsl:template match="book">                                               
    <h1>book</h1>                                                           
    <xsl:apply-templates  />                                                
  </xsl:template>                                                           
  <xsl:template match="chapter">                                            
    <h2>chapter <xsl:value-of select="."/></h2>                             
  </xsl:template>                                                           
</xsl:stylesheet>                                                           
""")                                                                            
style = libxslt.parseStylesheetDoc(styledoc)                                

print( style.applyStylesheet(doc, None) )                                                                                            

我错过了什么?

不确定为什么添加 <xsl:apply-templates/> 对您不起作用。 但是,您缺少输出 XML 的根元素。 此样式表:

<?xml version="1.0"?>
<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="book">
    <root>
        <h1>book</h1>
        <xsl:apply-templates/>
    </root>
</xsl:template>
<xsl:template match="chapter">
    <h2>chapter <xsl:value-of select="."/>
    </h2>
</xsl:template>
</xsl:stylesheet>          

会产生:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <h1>book</h1>
    <h2>chapter  1 </h2>
    <h2>chapter  2 </h2>
</root>

这看起来真的很奇怪 - 除非你意识到会发生什么。据我所知,这与 lxml 如何执行 XSLT 转换无关。

只是 lxml.etree.tostring() 需要一个包含 格式良好 HTML 或 XML 的对象作为输入。您不要 提交格式正确的标记:

<?xml version="1.0"?>
<h1>book</h1>                                                                          
      <h2>chapter  1 </h2>                                                        
      <h2>chapter  2 </h2>

因为你不这样做,所以它在第一个最外层(是的,有三个)元素之后停止。在我看来完全合理,不应该有任何理由不输出格式正确的 XHTML - 如果后面的内容不是 XML (如其他已指出)。

为了证明这一切,运行 下面的代码。唯一的变化是我只是 print 结果。

import lxml.etree
xml = lxml.etree.XML("""                                                    
    <book>                                                                          
      <chapter> 1 </chapter>                                                        
      <chapter> 2 </chapter>                                                        
    </book>                                                                         
""")                                                                            
transform = lxml.etree.XSLT( lxml.etree.XML("""                                  
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >

      <xsl:template match="book">                                                   
        <h1>book</h1>                                                               
        <xsl:apply-templates />                                                    
      </xsl:template>                                                               
      <xsl:template match="chapter">                                                
        <h2>chapter <xsl:value-of select="."/></h2>                                 
      </xsl:template>                                                               
    </xsl:stylesheet>                                                               
""") )                                                                                                                                                      
html = transform(xml)                                                            
print(html)

命令行的结果是

<?xml version="1.0"?>
<h1>book</h1>                                                                          
      <h2>chapter  1 </h2>                                                        
      <h2>chapter  2 </h2>
[EMPTY OUPUT LINE]      
[EMPTY OUPUT LINE]

而且,现在显而易见的是:

  • 使用 libxml2 和 libxslt 的代码可以工作,因为打印方法不同
  • 修改 XSLT 样式表以插入单个根元素是有效的,因为这样 tostring() 可以序列化格式良好的 XML.

使用lxml 3.4.1, Python sys.version 为2.7.5, Mac OS X.