XSLT 1.0 用于迭代多个具有逗号分隔值的 XML 元素

发布于 2024-10-30 14:11:43 字数 711 浏览 1 评论 0原文

我有一个 XML 文档,结构如下

<items>
 <item>
  <name>item1</name>
  <attributes>a,b,c,d</attributes>
 </item>
 <item>
  <name>item2</name>
  <attributes>c,d,e</attributes>
 </item>
</items>

对于每个唯一的属性值(以逗号分隔),我需要列出与该值关联的所有项目名称,如下所示:

a : item1
b : item1
c : item1, item2
d : item1, item2
e : item2

我最初的计划是使用模板将属性解析为属性节点,围绕每个属性使用适当的标签,然后使用 XPATH 表达式分离出唯一值

Attribute[not(.=following::Attribute)]

,但由于模板的结果不是经过 XML 解析器的节点集,因此我无法遍历它。我还尝试了 exslt 的 node-set() 函数,结果发现它也不允许我遍历各个属性节点。

此时,我不知道有什么简单的方法可以做到这一点,并且非常感谢有关如何继续的任何帮助或想法。谢谢!

I have an XML document structured as follows

<items>
 <item>
  <name>item1</name>
  <attributes>a,b,c,d</attributes>
 </item>
 <item>
  <name>item2</name>
  <attributes>c,d,e</attributes>
 </item>
</items>

For each unique attribute value (delimited by commas) I need to list all item names associated with that value like so:

a : item1
b : item1
c : item1, item2
d : item1, item2
e : item2

My initial plan was to use a template to parse the attributes into Attribute nodes, surrounding each with appropriate tags, and then separating out the unique values with an XPATH expression like

Attribute[not(.=following::Attribute)]

but since the result of the template isn't a node-set that ever goes through an XML parser, I can't traverse it. I also tried exslt's node-set() function only to realize it does not allow me to traverse the individual Attribute nodes either.

At this point I'm at a loss for a simple way to do this and would really appreciate any help or ideas on how to proceed. Thanks!

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

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

发布评论

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

评论(2

最美不过初阳 2024-11-06 14:11:43

此转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 xmlns:ext="http://exslt.org/common">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kAtrByVal" match="attr" use="."/>

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

  <xsl:variable name="vPass1"
       select="ext:node-set($vrtfPass1)"/>

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

 <xsl:template match="item">
  <group name="{name}">
   <xsl:apply-templates select="attributes"/>
  </group>
 </xsl:template>

 <xsl:template match="attributes" name="tokenize">
  <xsl:param name="pText" select="."/>

  <xsl:if test="string-length($pText)">
   <xsl:variable name="vText" select=
        "concat($pText,',')"/>
   <attr>
    <xsl:value-of select="substring-before($vText,',')"/>
   </attr>
   <xsl:call-template name="tokenize">
    <xsl:with-param name="pText" select=
    "substring-after($pText,',')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template match=
  "attr[generate-id()
       =
        generate-id(key('kAtrByVal',.)[1])
       ]
  ">
  <xsl:value-of select="concat('
',.,': ')"/>

  <xsl:for-each select="key('kAtrByVal',.)">
   <xsl:value-of select="../@name"/>
   <xsl:if test="not(position()=last())">
    <xsl:text>, </xsl:text>
   </xsl:if>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

应用于提供的 XML 文档时

<items>
    <item>
        <name>item1</name>
        <attributes>a,b,c,d</attributes>
    </item>
    <item>
        <name>item2</name>
        <attributes>c,d,e</attributes>
    </item>
</items>

产生所需的正确结果:

a: item1
b: item1
c: item1, item2
d: item1, item2
e: item2

说明

  1. Pass1:标记化和最终结果:

<groups>
  <group name="item1">
    <attr>a</attr>
    <attr>b</attr>
    <attr>c</attr>
    <attr>d</attr>
  </group>
  <group name="item2">
    <attr>c</attr>
    <attr>d</attr>
    <attr>e</attr>
  </group>
</groups>

.2。 Pass2 将 Pass1 的结果(使用扩展函数 ext:node-set() 转换为节点集)作为输入,执行 Muenchian 分组并生成最终的所需结果。

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 xmlns:ext="http://exslt.org/common">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kAtrByVal" match="attr" use="."/>

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

  <xsl:variable name="vPass1"
       select="ext:node-set($vrtfPass1)"/>

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

 <xsl:template match="item">
  <group name="{name}">
   <xsl:apply-templates select="attributes"/>
  </group>
 </xsl:template>

 <xsl:template match="attributes" name="tokenize">
  <xsl:param name="pText" select="."/>

  <xsl:if test="string-length($pText)">
   <xsl:variable name="vText" select=
        "concat($pText,',')"/>
   <attr>
    <xsl:value-of select="substring-before($vText,',')"/>
   </attr>
   <xsl:call-template name="tokenize">
    <xsl:with-param name="pText" select=
    "substring-after($pText,',')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template match=
  "attr[generate-id()
       =
        generate-id(key('kAtrByVal',.)[1])
       ]
  ">
  <xsl:value-of select="concat('
',.,': ')"/>

  <xsl:for-each select="key('kAtrByVal',.)">
   <xsl:value-of select="../@name"/>
   <xsl:if test="not(position()=last())">
    <xsl:text>, </xsl:text>
   </xsl:if>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

when applied on the provided XML document:

<items>
    <item>
        <name>item1</name>
        <attributes>a,b,c,d</attributes>
    </item>
    <item>
        <name>item2</name>
        <attributes>c,d,e</attributes>
    </item>
</items>

produces the wanted, correct result:

a: item1
b: item1
c: item1, item2
d: item1, item2
e: item2

Explanation:

  1. Pass1: tokenization and end result:

<groups>
  <group name="item1">
    <attr>a</attr>
    <attr>b</attr>
    <attr>c</attr>
    <attr>d</attr>
  </group>
  <group name="item2">
    <attr>c</attr>
    <attr>d</attr>
    <attr>e</attr>
  </group>
</groups>

.2. Pass2 takes the result of Pass1 (converted to a nodeset using the extension function ext:node-set()) as input, performs Muenchian grouping and produces the final, wanted result.

故乡的云 2024-11-06 14:11:43

我的第一个想法是进行两次传递。首先,使用(稍微)修改版本的 @Alejandro 对上一个问题的回答

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <items>
            <xsl:apply-templates/>
        </items>
    </xsl:template>
    <xsl:template match="item">
        <item name="{name}">
            <xsl:apply-templates select="attributes"/>
        </item>
    </xsl:template>
    <xsl:template match="attributes" name="tokenize">
        <xsl:param name="text" select="."/>
        <xsl:param name="separator" select="','"/>
        <xsl:choose>
            <xsl:when test="not(contains($text, $separator))">
                <val>
                    <xsl:value-of select="normalize-space($text)"/>
                </val>
            </xsl:when>
            <xsl:otherwise>
                <val>
                    <xsl:value-of select="normalize-space(
                        substring-before($text, $separator))"/>
                </val>
                <xsl:call-template name="tokenize">
                    <xsl:with-param name="text" select="substring-after(
                                    $text, $separator)"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

生成:

<items>
    <item name="item1">
        <val>a</val>
        <val>b</val>
        <val>c</val>
        <val>d</val>
    </item>
    <item name="item2">
        <val>c</val>
        <val>d</val>
        <val>e</val>
    </item>
</items>

然后将以下样式表应用于该输出:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="byVal" match="val" use="." />
    <xsl:template match="val[generate-id() = 
                             generate-id(key('byVal', .)[1])]">
        <xsl:value-of select="." />
        <xsl:text> : </xsl:text>
        <xsl:apply-templates select="key('byVal', .)" mode="group" />
        <xsl:text>
</xsl:text>
    </xsl:template>
    <xsl:template match="val" mode="group">
        <xsl:value-of select="../@name" />
        <xsl:if test="position() != last()">
            <xsl:text>, </xsl:text>
        </xsl:if> 
    </xsl:template>
    <xsl:template match="val" />
</xsl:stylesheet>

生成:

a : item1
b : item1
c : item1, item2
d : item1, item2
e : item2

在一个样式表中执行此操作需要更多思考(或扩展函数)。

My first thought is to make two passes. First, tokenize the attributes elements using a (slightly) modified version of @Alejandro's answer to this previous question:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <items>
            <xsl:apply-templates/>
        </items>
    </xsl:template>
    <xsl:template match="item">
        <item name="{name}">
            <xsl:apply-templates select="attributes"/>
        </item>
    </xsl:template>
    <xsl:template match="attributes" name="tokenize">
        <xsl:param name="text" select="."/>
        <xsl:param name="separator" select="','"/>
        <xsl:choose>
            <xsl:when test="not(contains($text, $separator))">
                <val>
                    <xsl:value-of select="normalize-space($text)"/>
                </val>
            </xsl:when>
            <xsl:otherwise>
                <val>
                    <xsl:value-of select="normalize-space(
                        substring-before($text, $separator))"/>
                </val>
                <xsl:call-template name="tokenize">
                    <xsl:with-param name="text" select="substring-after(
                                    $text, $separator)"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

Which produces:

<items>
    <item name="item1">
        <val>a</val>
        <val>b</val>
        <val>c</val>
        <val>d</val>
    </item>
    <item name="item2">
        <val>c</val>
        <val>d</val>
        <val>e</val>
    </item>
</items>

Then apply the following stylesheet to that output:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="byVal" match="val" use="." />
    <xsl:template match="val[generate-id() = 
                             generate-id(key('byVal', .)[1])]">
        <xsl:value-of select="." />
        <xsl:text> : </xsl:text>
        <xsl:apply-templates select="key('byVal', .)" mode="group" />
        <xsl:text>
</xsl:text>
    </xsl:template>
    <xsl:template match="val" mode="group">
        <xsl:value-of select="../@name" />
        <xsl:if test="position() != last()">
            <xsl:text>, </xsl:text>
        </xsl:if> 
    </xsl:template>
    <xsl:template match="val" />
</xsl:stylesheet>

Producing:

a : item1
b : item1
c : item1, item2
d : item1, item2
e : item2

Doing this in one stylesheet would require more thought (or an extension function).

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