如何使用 XSLT 对值进行排序?

发布于 2024-12-07 03:40:31 字数 670 浏览 0 评论 0原文

我有一个与此类似的 XML:

<Test>
  <grapes>
     <a>TypeA</a>
     <b>value1</b>
  </grapes>
  <oranges>
     <a>TypeB</a>
     <b>value2</b>
  </oranges>
  <apples>
    <a>TypeA</a>
     <b>value3</b>
  </apples>
</Test>

其中值是唯一的,但类型可能相同。

我试图对其进行排序,以便输出与此类似:

<group type="TypeA">
  <value v="value1" />
  <value v="value3" />
</group>
<group type="TypeB">
  <value v="value2" />
</group>

我很难确保输出中的组是唯一的并且值位于正确的组中。

我的 XSL 应该如何构建?

I have an XML similar to this:

<Test>
  <grapes>
     <a>TypeA</a>
     <b>value1</b>
  </grapes>
  <oranges>
     <a>TypeB</a>
     <b>value2</b>
  </oranges>
  <apples>
    <a>TypeA</a>
     <b>value3</b>
  </apples>
</Test>

where the values are unique but the Type, might be the same.

I am trying to sort it so that the output is similar to this:

<group type="TypeA">
  <value v="value1" />
  <value v="value3" />
</group>
<group type="TypeB">
  <value v="value2" />
</group>

I am having a hard time making sure the groups are unique in the output and the values are in the right group.

How should my XSL be structured?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

柠栀 2024-12-14 03:40:31

这是一个更简单的解决方案(完全“推送式”,没有,没有嵌套,没有,无 current(),,无 //,无轴):

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

 <xsl:key name="kGoodsByType" match="/*/*" use="a"/>

 <xsl:template match=
  "/*/*[generate-id()
       =
        generate-id(key('kGoodsByType', a)[1])
       ]
  ">
     <group type="{a}">
       <xsl:apply-templates select="key('kGoodsByType', a)/b"/>
     </group>
 </xsl:template>

 <xsl:template match="b">
  <value v="{.}"/>
 </xsl:template>

 <xsl:template match="*/* | text()" priority="-1"/>
</xsl:stylesheet>

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

<Test>
    <grapes>
        <a>TypeA</a>
        <b>value1</b>
    </grapes>
    <oranges>
        <a>TypeB</a>
        <b>value2</b>
    </oranges>
    <apples>
        <a>TypeA</a>
        <b>value3</b>
    </apples>
</Test>

通缉者,产生正确的结果

<group type="TypeA">
   <value v="value1"/>
   <value v="value3"/>
</group>
<group type="TypeB">
   <value v="value2"/>
</group>

解释 /*/* 的慕尼黑分组,使用其 a 子项的字符串值作为键。

二. 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:template match="/*">
  <xsl:for-each-group select="*/a" group-by=".">
   <group type="{current-grouping-key()}">
     <xsl:sequence select="current-group()/../b"/>
   </group>
  </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

当对同一个 XML 文档(上面)执行此转换时,会产生相同的正确结果

<group type="TypeA">
   <b>value1</b>
   <b>value3</b>
</group>
<group type="TypeB">
   <b>value2</b>
</group>

说明

  1. current-group() >

  2. current-grouping-key()

Here is a much simpler solution (completely "push style", no <xsl:for-each>, no nesting, no <xsl:variable>, no current(), , no //, no axes):

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

 <xsl:key name="kGoodsByType" match="/*/*" use="a"/>

 <xsl:template match=
  "/*/*[generate-id()
       =
        generate-id(key('kGoodsByType', a)[1])
       ]
  ">
     <group type="{a}">
       <xsl:apply-templates select="key('kGoodsByType', a)/b"/>
     </group>
 </xsl:template>

 <xsl:template match="b">
  <value v="{.}"/>
 </xsl:template>

 <xsl:template match="*/* | text()" priority="-1"/>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<Test>
    <grapes>
        <a>TypeA</a>
        <b>value1</b>
    </grapes>
    <oranges>
        <a>TypeB</a>
        <b>value2</b>
    </oranges>
    <apples>
        <a>TypeA</a>
        <b>value3</b>
    </apples>
</Test>

the wanted, correct result is produced:

<group type="TypeA">
   <value v="value1"/>
   <value v="value3"/>
</group>
<group type="TypeB">
   <value v="value2"/>
</group>

Explanation: Muenchian grouping of /*/* using as key the string values of their a children.

II. XSLT 2.0 solution:

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

 <xsl:template match="/*">
  <xsl:for-each-group select="*/a" group-by=".">
   <group type="{current-grouping-key()}">
     <xsl:sequence select="current-group()/../b"/>
   </group>
  </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

When this transformation is performed on the same XML document (above), the same correct result is produced:

<group type="TypeA">
   <b>value1</b>
   <b>value3</b>
</group>
<group type="TypeB">
   <b>value2</b>
</group>

Explanation:

  1. <xsl:for-each-group>

  2. current-group()

  3. current-grouping-key()

花期渐远 2024-12-14 03:40:31

XSLT 1.0:

首先使用 muenchian 方法为您的类型创建唯一的组。谷歌一下就知道它是什么。然后,只需迭代它们并打印出您想要的内容、您想要的方式:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="types" match="a" use="text()"/>
<xsl:template match="/">
<result>
  <xsl:for-each select="//a[generate-id(.) = generate-id(key('types', text())[1])]">
    <group type="{current()/text()}">
      <xsl:for-each select="//a[text() = current()/text()]">
        <xsl:variable name="values" select="following-sibling::b | preceding-sibling::b"/>
        <xsl:for-each select="$values">
          <value v="{current()}"/>
        </xsl:for-each>
      </xsl:for-each>
    </group>
  </xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>

您会发现输出与您期望的相同。

XSLT 1.0 :

You start by creating unique groups for your types using the muenchian method. Google it to find out what it is. Then it's just a matter of iterating through them and printint out what you want, how you want it :

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="types" match="a" use="text()"/>
<xsl:template match="/">
<result>
  <xsl:for-each select="//a[generate-id(.) = generate-id(key('types', text())[1])]">
    <group type="{current()/text()}">
      <xsl:for-each select="//a[text() = current()/text()]">
        <xsl:variable name="values" select="following-sibling::b | preceding-sibling::b"/>
        <xsl:for-each select="$values">
          <value v="{current()}"/>
        </xsl:for-each>
      </xsl:for-each>
    </group>
  </xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>

You will find that the output is identical to what you expect.

口干舌燥 2024-12-14 03:40:31

我的建议与 FailedDev 的略有不同:

<?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:template match="/Test">
    <root>
      <xsl:for-each select="*[a != following::a]">
        <xsl:sort select="a" data-type="text" />
        <group type="{a}">
          <xsl:for-each select="/Test/*[a = current()/a]">
            <xsl:sort select="b" data-type="text" />
            <value v="{b}" />
          </xsl:for-each>
        </group>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

外部

<xsl:for-each select="*[a != following::a]" />

选择所有唯一类型,即。 类型A类型B
根据名称对它们进行排序

<xsl:sort select="a" data-type="text" />

,确保 TypeA 将出现在 TypeB 上方。
内部

<xsl:for-each select="/Test/*[a = current()/a]" />

为每种类型选择唯一值的列表,即。对于TypeA,提取值value1value3。同样,结果列表排序为在 value3 之前列出 value1

My proposal is slightly different from FailedDev's:

<?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:template match="/Test">
    <root>
      <xsl:for-each select="*[a != following::a]">
        <xsl:sort select="a" data-type="text" />
        <group type="{a}">
          <xsl:for-each select="/Test/*[a = current()/a]">
            <xsl:sort select="b" data-type="text" />
            <value v="{b}" />
          </xsl:for-each>
        </group>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

The outer

<xsl:for-each select="*[a != following::a]" />

select all unique types, ie. TypeA and TypeB.
The

<xsl:sort select="a" data-type="text" />

sorts these according to name, making sure TypeA will appear above TypeB.
The inner

<xsl:for-each select="/Test/*[a = current()/a]" />

selects a list of unique values for each type, ie. for TypeA the values value1 and value3 are extracted. Again, the resulting list is sorted to list value1 before value3.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文