【问题标题】:Printing list of elements with atributes from xml document using XSLT使用 XSLT 从 xml 文档中打印具有属性的元素列表
【发布时间】:2011-11-01 22:25:09
【问题描述】:

我需要解决一个非常奇怪的问题。我需要一个 XSLT 样式表,它将打印结构未知的 xml 文档的元素列表及其属性。经过多次尝试,我设法创造了这样一个东西:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<HTML>
<title></title>
<body>  

<xsl:call-template name="recurs">
<xsl:with-param name="nextnodes" select="child::*" />
</xsl:call-template>

</body>
</HTML>
</xsl:template>

<xsl:template name="recurs">
<xsl:param name="nextnodes" />
<xsl:for-each select="$nextnodes">
<xsl:if test="not(name(current())=name(following::*)) and  not(name(current())=name(following::*/descendant::*)) ">
    Element <b><xsl:value-of select="name(current())" /></b> has attributes <text> </text>
    <xsl:for-each select="@*">
    <xsl:if test="position()=last()">
    <b><xsl:value-of select="name(current())" /><text>.</text></b>
    </xsl:if>
    <xsl:if test="position()!=last()">
        <b><xsl:value-of select="name(current())" /><text>,  </text></b>
    </xsl:if>
    </xsl:for-each>
    <br /><br />
</xsl:if>
<xsl:call-template name="recurs">
    <xsl:with-param name="nextnodes" select="child::*" />
</xsl:call-template>

</xsl:for-each> 
</xsl:template>

</xsl:stylesheet>

对于这样的测试用例,当元素书再次出现在其他元素中时,它可以正常工作:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="task3_4a.xsl"?>
<catalog subnodes="2">

<cities country="England">
<city name="London" region="London" population="10000" />
<city name="New South Wales" region="Wales" population="800000" />

</cities> 

<articles country="USA">
<article name="My lovely country" src="art1.txt" />
<article name="Places to visit" src="art2.txt" />
<article name="Article 3" src="art3.txt" />
</articles>

<books>
<book title="Warhammer">
</book>
<book title="We fought for truth"> 
</book>
</books>

<scientifics  atr = " ">
<book title="Warhammer">
</book> 
</scientifics>
</catalog>

但是当我尝试另一个测试时,书籍内有元素文章,它无法正确管理 xml:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="task3_4a.xsl"?>
<catalog subnodes="2">

<cities country="England">
<city name="London" region="London" population="10000" />
<city name="New South Wales" region="Wales" population="800000" />

</cities> 

<articles country="USA">
<article name="My lovely country" src="art1.txt" />
<article name="Places to visit" src="art2.txt" />
<article name="Article 3" src="art3.txt" />
</articles>

<books>
<book title="Warhammer">
<article name="My lovely country" src="art1.txt" /> 
</book>
<book title="We fought for truth"> 
<article name="My lovely country" src="art1.txt" /> 
</book>
</books>

<scientifics  atr = " ">
<book title="Warhammer">

<article name="My lovely country" src="art1.txt" /> 
</book> 

</scientifics>

</catalog>

输出现在包含字符串“元素文章具有属性名称,src。” 3次。而且我不知道如何解决它......

【问题讨论】:

  • 它遍历 3 个可能的节点层次结构以到达
    节点。这就是为什么它显示3次。您需要跟踪以某种方式找到的结构,然后进行比较以确保您没有重复。 1.
    2.
    3.
  • 您需要打印出每个独特的元素及其属性吗?还是所有元素?
  • 我需要将文档中的所有元素与它们的属性一起打印,但是尽管某些元素在不同层次结构级别的文档中出现的次数不止一次,但对于任何此类,输出列表中应该只有一个项目元素。
  • @ferbolg 对于必须累积并与以前的值进行比较的那种操作(只是为了查看是否已经显示),XSLT 可能不是最佳解决方案。 XSLT 非常适合格式化和转换,但对于计数和比较,使用支持地图/字典的脚本语言可能会更好
  • 我现在意识到某种 DOM 或 SAX 解析会更好,但问题是我需要一个 XSLT 样式表...

标签: xml xslt


【解决方案1】:

我。这是一个非常简短的 XSLT 2.0 解决方案

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

 <xsl:template match="/">
    <xsl:for-each-group select="//*"
         group-by="string-join((name(), @*/name()), '|')">
      <xsl:sort select="name()"/>

      <p>
        Element <b><xsl:sequence select="name()"/></b>
        has attributes: <xsl:value-of select="@*/name()" separator=", "/>
      </p>
  </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时

<catalog subnodes="2">
    <cities country="England">
        <city name="London" region="London" population="10000" />
        <city name="New South Wales" region="Wales" population="800000" />
    </cities>
    <articles country="USA">
        <article name="My lovely country" src="art1.txt" />
        <article name="Places to visit" src="art2.txt" />
        <article name="Article 3" src="art3.txt" />
    </articles>
    <books>
        <book title="Warhammer">
            <article name="My lovely country" src="art1.txt" />
        </book>
        <book title="We fought for truth">
            <article name="My lovely country" src="art1.txt" />
        </book>
    </books>
    <scientifics  atr = " ">
        <book title="Warhammer">
            <article name="My lovely country" src="art1.txt" />
        </book>
    </scientifics>
</catalog>

产生了想要的正确结果

<p>
        Element <b>article</b>
        has attributes: name, src</p>
<p>
        Element <b>articles</b>
        has attributes: country</p>
<p>
        Element <b>book</b>
        has attributes: title</p>
<p>
        Element <b>books</b>
        has attributes: </p>
<p>
        Element <b>catalog</b>
        has attributes: subnodes</p>
<p>
        Element <b>cities</b>
        has attributes: country</p>
<p>
        Element <b>city</b>
        has attributes: name, region, population</p>
<p>
        Element <b>scientifics</b>
        has attributes: atr</p>

在浏览器中显示为

元素文章 有属性:name,src

元素 文章 有属性:国家

元素 有属性:title

元素书籍 有属性:

元素目录 有属性:子节点

元素城市 有属性:国家

元素城市 具有属性:名称、地区、人口

元素科学 有属性:atr

二。 XSLT 1.0(两遍)解决方案:

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

 <xsl:key name="kElByNameandAttrs" match="*"
  use="concat(name(), '|', @_____attribs)"/>

 <xsl:variable name="vrtfPass1">
  <xsl:apply-templates/>
 </xsl:variable>

 <xsl:template match="*">
  <xsl:copy>
   <xsl:attribute name="_____attribs">
     <xsl:for-each select="@*">
       <xsl:sort select="name()"/>

       <xsl:value-of select="concat(name(), ' ')"/>
     </xsl:for-each>
   </xsl:attribute>

   <xsl:apply-templates select="*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="/">
   <xsl:apply-templates mode="pass2" select=
   "ext:node-set($vrtfPass1)//*
          [generate-id()
          =
           generate-id(key('kElByNameandAttrs',
                           concat(name(),
                                  '|',
                                  @_____attribs)
                           )
                            [1])
           ]"
   >
    <xsl:sort select="name()"/>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="*" mode="pass2">
  <p>
        Element <b><xsl:value-of select="name()"/></b>
        has attributes: <xsl:value-of select="@_____attribs"/></p>
 </xsl:template>

</xsl:stylesheet>

当这个 XSLT 1.0 转换应用于同一个 XML 文档(上图)时,再次产生所需的正确结果

<p>
        Element <b>article</b>
        has attributes: name src </p>
<p>
        Element <b>articles</b>
        has attributes: country </p>
<p>
        Element <b>book</b>
        has attributes: title </p>
<p>
        Element <b>books</b>
        has attributes: </p>
<p>
        Element <b>catalog</b>
        has attributes: subnodes </p>
<p>
        Element <b>cities</b>
        has attributes: country </p>
<p>
        Element <b>city</b>
        has attributes: name population region </p>
<p>
        Element <b>scientifics</b>
        has attributes: atr </p>

【讨论】:

  • @Dimitri Novatchev 非常好的解决方案 +1。您能否解释一下:group-by="string-join((name(), @*/name()), '|')">?据我了解,您按名称和所有属性分组?这也可以通过 XSLT 1.0 中的 muenchian 分组来完成吗?
  • @FailedDev:回复:group-by="string-join((name(), @*/name()), '|')"。这按一个字符串分组,该字符串是元素名称及其所有属性的 names 的串联——所有这些都由一个竖线字符连接。顺便说一句,我也添加了 XSLT 1.0 解决方案。 :)
  • @Dimitri Novatchev 很棒的东西 :) 很高兴学到新东西。
【解决方案2】:

您遇到的一个问题是使用此 XPath

<xsl:if test="not(name(current())=name(following::*)) ...

使用轴 following:: 将返回多个节点,但应用 name() 函数只会获取第一个节点的名称。

所以,而不是 XSLT 中的以下行>...

<xsl:if test="not(name(current())=name(following::*)) and  not(name(current())=name(following::*/descendant::*)) ">  

尝试将其替换为以下行...

<xsl:if test="not(following::*[name() = name(current())])">

即没有与当前节点同名的后续节点(在层次结构的任何级别中)。

执行此操作时,将输出以下内容:

<HTML>
<title></title>
<body>
Element <b>catalog</b> has attributes <text></text><b>subnodes<text>.</text></b><br><br>
Element <b>cities</b> has attributes <text></text><b>country<text>.</text></b><br><br>
Element <b>city</b> has attributes <text></text><b>name<text>,  </text></b><b>region<text>,  </text></b><b>population<text>.</text></b><br><br>
Element <b>articles</b> has attributes <text></text><b>country<text>.</text></b><br><br>
Element <b>books</b> has attributes <text></text><br><br>
Element <b>scientifics</b> has attributes <text></text><b>atr<text>.</text></b><br><br>
Element <b>book</b> has attributes <text></text><b>title<text>.</text></b><br><br>
Element <b>article</b> has attributes <text></text><b>name<text>,  </text></b><b>src<text>.</text></b><br><br></body>
</HTML>

当然,这并不能解决两个同名元素具有不同属性时的匹配问题,但它应该可以解决您的文章出现多次的直接问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多