使用 XSLT 检索 XML 中的属性值

Retrieving value of attributes in XML using XSLT

要扩展提供的解决方案 here,目的是检索属性的值及其名称。

对于此文档,

<a>
    <apple color="red"/>
    <apple color="green"/>
    <banana color="yellow"/>
    <sugar taste="sweet"/>
    <cat size="small"/>
</a>

上面post中的代码提供了这个结果:

For tags: apple 2, banana 1, sugar 1, cat 1 
For attributes: color 3, taste 1, size 1 

现在,期望的结果是:

For tags: apple 2, banana 1, sugar 1, cat 1 
For attributes: color(red) 1, color(green) 1, color(yellow) 1, taste(sweet) 1, size (small) 1

非常感谢。

要扩展 Rudramuni TP 提供的 solution for your previous question,您可以添加一个额外的键来获取唯一的属性值:

<xsl:key name="kAttribValue" match="@*" use="."/>

和一个附加变量:

<xsl:variable name="var2">
  <xsl:for-each select="/a/*/@*[generate-id() = generate-id(key('kAttribValue', .))]">
     <xsl:value-of select="concat(name(.), '(', .,')', ' ', count(key('kAttribValue', .)))"/>
       <xsl:if test="not(position()=last())">
         <xsl:text>, </xsl:text>
       </xsl:if>
  </xsl:for-each>
</xsl:variable>

通过对 Rudramuni TP 代码的这些调整:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
  <xsl:key name="kEleName" match="*" use="local-name()"/>
  <xsl:key name="kAttribName" match="@*" use="local-name()"/>
  <xsl:key name="kAttribValue" match="@*" use="."/>

  <xsl:variable name="var1">
    <xsl:for-each select="/a/*/@*[generate-id() = generate-id(key('kAttribName', name()))]">
      <xsl:value-of select="concat(name(.), ' ', count(key('kAttribName', name(.))))"/>
      <xsl:if test="not(position()=last())">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>
  <xsl:variable name="var2">
    <xsl:for-each select="/a/*/@*[generate-id() = generate-id(key('kAttribValue', .))]">
      <xsl:value-of select="concat(name(.), '(', .,')', ' ', count(key('kAttribValue', .)))"/>
      <xsl:if test="not(position()=last())">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/*">
    <xsl:apply-templates select="*[generate-id() = generate-id(key('kEleName', name()))]"/>
  </xsl:template>
  <xsl:template match="*">
    <xsl:if test="position()=1"><xsl:text>For tags: </xsl:text></xsl:if>
        <xsl:value-of select="concat(name(.), ' ', count(key('kEleName', name(.))))"/>
        <xsl:if test="following-sibling::*">
          <xsl:text>, </xsl:text>
        </xsl:if>
        <xsl:if test="position()=last()">
          <xsl:text>&#10;For attributes: </xsl:text>
        <xsl:value-of select="$var2"/>
     </xsl:if>
  </xsl:template>
</xsl:stylesheet>

应用于您的输入时 XML

<a>
  <apple color="red"/>
  <apple color="green"/>
  <banana color="yellow"/>
  <sugar taste="sweet"/>
  <cat size="small"/>
</a>

生成扩展结果:

For tags: apple 2, banana 1, sugar 1, cat 1
For attributes: color(red) 1, color(green) 1, color(yellow) 1, taste(sweet) 1, size(small) 1

更新: 如评论中所述,上述调整(以及上一个问题的答案)仅适用于问题中提供的输入。给定输入 XML,例如

<root>
  <a>
    <apple color="red"/>
    <apple color="green"/>
    <banana color="yellow"/>
    <sugar taste="sweet"/>
    <cat size="small"/>
  </a>
  <a>
    <apple color="red"/>
    <apple color="green"/>
    <banana color="yellow"/>
    <sugar taste="sweet"/>
    <dog size="big"/>
  </a>
</root> 

模板可以调整如下:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
  <xsl:key name="kEleName" match="*" use="local-name()"/>
  <xsl:key name="kAttribName" match="@*" use="local-name()"/>
  <xsl:key name="kAttribValue" match="@*" use="."/>

  <xsl:variable name="var1">
    <xsl:for-each select="//a/*/@*[generate-id() = generate-id(key('kAttribValue', .))]">
      <xsl:value-of select="concat(name(.), ' (', .,')', ' ', count(key('kAttribValue', .)))"/>
      <xsl:if test="not(position()=last())">
        <xsl:text>, </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/*">
    <xsl:apply-templates select="a/*[generate-id() = generate-id(key('kEleName', name()))]"/>
  </xsl:template>
  <xsl:template match="*">
    <xsl:if test="position()=1"><xsl:text>For tags: </xsl:text></xsl:if>
      <xsl:value-of select="concat(name(.), ' ', count(key('kEleName', name(.))))"/>
      <xsl:if test="following::*">
        <xsl:text>, </xsl:text>
      </xsl:if>
      <xsl:if test="position()=last()">
        <xsl:text>&#10;For attributes: </xsl:text>
        <xsl:value-of select="$var1"/>
      </xsl:if>
  </xsl:template>
</xsl:stylesheet>

将其应用于调整后的示例输入时 XML 会生成输出:

For tags: apple 4, banana 2, sugar 2, cat 1, dog 1
For attributes: color (red) 2, color (green) 2, color (yellow) 2, taste (sweet) 2, size (small) 1, size (big) 1

调整如下:本次

<xsl:for-each select="/a/*/@*[generate-id() = generate-id(key('kAttribValue', .))]">

必须调整为

<xsl:for-each select="//a/*/@*[generate-id() = generate-id(key('kAttribValue', .))]">

否则只有第一个 a 节点的子节点的属性被匹配。

这个

<xsl:apply-templates select="*[generate-id() = generate-id(key('kEleName', name()))]"/>

必须调整为

<xsl:apply-templates select="a/*[generate-id() = generate-id(key('kEleName', name()))]"/>

否则将选择 a 标签(这将产生输出 For tags: a 2,)。

还有这个

<xsl:if test="following-sibling::*">
  <xsl:text>, </xsl:text>
</xsl:if>

必须调整为

<xsl:if test="following::*">
  <xsl:text>, </xsl:text>
</xsl:if>

因为在第二个 a 节点中添加的 dog 不是 following sibling 但仍然是 following元素。

很明显,如果未知要求允许具有不同的命名父节点,则此调整将不会工作 - 作为简化的示例,它不适用于像[=这样的输入32=]

<a>
  <apple color="red"/>
  <apple color="green"/>
</a>
<b>
  <banana color="yellow"/>
</b>

如果不同的父节点或嵌套结构,例如

<a>
  <apple color="red"/>
  <apple color="green"/>
  <b>
    <banana color="yellow"/>
  </b>
</a>

也应该处理,我建议为此提出一个新问题,因为这与问题中提供的示例输入 XML 差异太大。