XSLT concat字符串,删除最后一个逗号

XSLT concat字符串,删除最后一个逗号,xslt,concatenation,Xslt,Concatenation,我需要使用XSLT构建一个字符串,并用逗号分隔每个字符串,但不要在最后一个字符串后包含逗号。在我下面的示例中,如果我有分发节点而不是Note节点,那么后面会有一个逗号。我不知道如何构建一个字符串作为变量,然后截断XSLT中的最后一个字符。这也是使用MicrosoftXSLT引擎 我的字符串= <xsl:if test="Locality != ''"> <xsl:value-of select="Locality"/>, </xsl:if> <xsl

我需要使用XSLT构建一个字符串,并用逗号分隔每个字符串,但不要在最后一个字符串后包含逗号。在我下面的示例中,如果我有分发节点而不是Note节点,那么后面会有一个逗号。我不知道如何构建一个字符串作为变量,然后截断XSLT中的最后一个字符。这也是使用MicrosoftXSLT引擎

我的字符串=

<xsl:if test="Locality != ''">
  <xsl:value-of select="Locality"/>,
</xsl:if>
<xsl:if test="CollectorAndNumber != ''">
  <xsl:value-of select="CollectorAndNumber"/>,
</xsl:if>
<xsl:if test="Institution != ''">
  <xsl:value-of select="Institution"/>,
</xsl:if>
<xsl:if test="Distribution != ''">
  <xsl:value-of select="Distribution"/>,
</xsl:if>
<xsl:if test="Note != ''">
  <xsl:value-of select="Note"/>
</xsl:if>

[老兄,一定有更好的方法进入这个问题文本框:

你没有一个总是存在的值吗?如果你这样做了,你可以把它转过来,把逗号放在除第一个项目之外的所有项目前面,第一个项目是你的价值,它总是在那里。

这会有点混乱,但如果只有一些元素,比如你的示例中的元素,这可能会奏效:

<xsl:if test="Locality != ''">
    <xsl:value-of select="Locality"/>
    <xsl:if test="CollectorAndNumber != '' or Institution != '' or Distribution != '' or Note != ''">
        <xsl:value-of select="','"/>
    </xsl:if>
</xsl:if>
<xsl:if test="CollectorAndNumber != ''">
    <xsl:value-of select="CollectorAndNumber"/>
    <xsl:if test="Institution != '' or Distribution != '' or Note != ''">
        <xsl:value-of select="','"/>
    </xsl:if>
</xsl:if>
<xsl:if test="Institution != ''">
    <xsl:value-of select="Institution"/>
    <xsl:if test="Distribution != '' or Note != ''">
        <xsl:value-of select="','"/>
    </xsl:if>
</xsl:if>
<xsl:if test="Distribution != ''">
    <xsl:value-of select="Distribution"/>
    <xsl:if test="Note != ''">
        <xsl:value-of select="','"/>
    </xsl:if>
</xsl:if>
<xsl:if test="Note != ''">
    <xsl:value-of select="Note"/>
</xsl:if>

假设您有类似以下输入XML的内容:

<root>
  <record>
    <Locality>Locality</Locality>
    <CollectorAndNumber>CollectorAndNumber</CollectorAndNumber>
    <Institution>Institution</Institution>
    <Distribution>Distribution</Distribution>
    <Note>Note</Note>
    <OtherStuff>Unimportant</OtherStuff>
  </record>
</root>

我更喜欢使用短调用模板将节点值连接在一起。如果连接列表中的一个节点(例如机构)丢失,则这也可以工作:

<xsl:template name="join">
    <xsl:param name="list" />
    <xsl:param name="separator"/>

    <xsl:for-each select="$list">
        <xsl:value-of select="." />
        <xsl:if test="position() != last()">
            <xsl:value-of select="$separator" />
        </xsl:if>
    </xsl:for-each>
</xsl:template>
可按如下方式使用:

fn:string-join({Locality, CollectorAndNumber, Distribution, Note}, ",") 
使用XSLT很容易做到这一点,无需在变量中捕获结果,或使用特殊命名模板:

一、XSLT1.0:

如果想要的元素不是按文档顺序生成的,那么问题中不需要,但Tomalak提出了一些,那么实现这一点仍然非常简单和优雅:

二,。XSLT2.0:

在XSLT中,此任务更简单,不需要特殊函数:


请注意,所需的元素将以任何所需的顺序生成,因为我们在XSLT 1.0解决方案中使用的是XPath 2.0序列类型与union,根据定义,它包含任何所需指定顺序的项。

我认为这可能是有用的, 当我使用复杂的选择时,位置不正确 过滤一些节点, 在这种情况下,我想出了以下窍门:

您可以定义一个字符串变量,该变量保存节点的值,以分隔 通过特定字符,然后使用str:tokenize 您可以创建一个完整的节点列表,该位置可以很好地使用它

大概是这样的:

<!-- Since position() doesn't work as expected(returning node position of current 
node list), I got round it by a string variable and tokenizing it in which 
absolute position is equal to relative(context) position. -->
<xsl:variable name="measObjLdns" >
    <xsl:for-each select="h:measValue[@measObjLdn=$currentMeasObjLdn]/h:measResults" >
        <xsl:value-of select="concat(.,'---')"/> <!-- is an optional separator. -->
    </xsl:for-each>
</xsl:variable>

<xsl:for-each select="str:tokenize($measObjLdns,'---')" ><!-- Since position() doesn't 

work as expected(returning node position of current node list), 
I got round it by a string variable and tokenizing it in which
absolute position is equal to relative(context) position. -->

    <xsl:value-of select="."></xsl:value-of>
    <xsl:if test="position() != last()">
        <xsl:text>,</xsl:text>
    </xsl:if>
</xsl:for-each>
<xsl:if test="position() != last()">
    <xsl:text>,</xsl:text>
</xsl:if>

@克雷格:我个人觉得SO文本编辑器至少是我使用过的最好的基于网络的编辑器。你不喜欢它的什么地方?@Weblog我大体上同意你的看法。一个例外是一个令人讨厌的错误,当SO代码编辑器认为XPath中的谓词(如myElem[1])是超链接,并根据文本中已有的实际链接数将其更改为类似myElem[5]的内容时。这是否意味着XSLT 1.0输出中的文档顺序?我故意使用变量来自由地按我喜欢的方式对输出进行排序。@Tomalak这个问题并不清楚,在提供的XML文档中,文档顺序与想要的顺序匹配。如果需要不同的输出顺序,请注意XSLT2.0解决方案不依赖于文档顺序。对于与文档顺序无关的XSLT 1.0解决方案,我更希望在某个参数中提供元素名称列表,然后仍然可以使用单个元素,或者我一直使用XSLT 1.0,但您已经解决了这个问题。谢谢对您的方法的一个改进是修改IF to be@Craig实际上,这是不需要的。如果文本长度为零,则文本测试不会成功。@Dimitre Novatchev:虽然原始问题中没有指定自定义顺序,但我认为这并不太牵强。事实上,您的两个1.0解决方案都很好。我会亲自发布您的XSLT1.0解决方案1,毕竟这是显而易见的,但文档顺序独立性对我来说很重要+1用于XSLT 1.0解决方案2和XSLT 2.0变体。我编辑了我的答案,以便按照您的建议为任意列表和元素排序提供解决方案。谢谢你的答案很像迪米特里。不过,他的发型要优雅一点。谢谢。解释一下你的代码可能有助于重用它。我对一些节点的过滤也有类似的问题。但无法给出您的答案,尤其是在一天结束时与xslt的怪癖作斗争时:-
<?xml version="1.0" encoding="utf-8"?>
<items>
  <item>
    <Locality>locality1</Locality>
    <CollectorAndNumber>collectorAndNumber1</CollectorAndNumber>
    <Distribution>distribution1</Distribution>
    <Note>note1</Note>
  </item>
  <item>
    <Locality>locality2</Locality>
    <CollectorAndNumber>collectorAndNumber2</CollectorAndNumber>
    <Institution>institution2</Institution>
    <Distribution>distribution2</Distribution>
    <Note>note2</Note>
  </item>
  <item>
    <Locality>locality3</Locality>
    <CollectorAndNumber>collectorAndNumber3</CollectorAndNumber>
    <Institution>institution3</Institution>
    <Distribution>distribution3</Distribution>
  </item>
</items>
<?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" indent="yes"/>

  <xsl:template match="/">
    <summary>
      <xsl:apply-templates />
    </summary>
  </xsl:template>

  <xsl:template match="item">
    <item>
      <xsl:call-template name="join">
        <xsl:with-param name="list" select="Locality | CollectorAndNumber | Institution | Distribution | Note" />
        <xsl:with-param name="separator" select="','" />
      </xsl:call-template>
    </item>
  </xsl:template>

  <xsl:template name="join">
    <xsl:param name="list" />
    <xsl:param name="separator"/>

    <xsl:for-each select="$list">
      <xsl:value-of select="." />
      <xsl:if test="position() != last()">
        <xsl:value-of select="$separator" />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="utf-8"?>
<summary>
  <item>locality1,collectorAndNumber1,distribution1,note1</item>
  <item>locality2,collectorAndNumber2,institution2,distribution2,note2</item>
  <item>locality3,collectorAndNumber3,institution3,distribution3</item>
</summary>
fn:string-join**($operand1 as string*, $operand2 as string*) as string
fn:string-join({Locality, CollectorAndNumber, Distribution, Note}, ",") 
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:template match="/*/*">
      <xsl:for-each select=
      "Locality/text() | CollectorAndNumber/text()
     | Institution/text() | Distribution/text()
     | Note/text()
      "
      >
        <xsl:value-of select="."/>
        <xsl:if test="not(position() = last())">,</xsl:if>
      </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>
<root>
    <record>
        <Locality>Locality</Locality>
        <CollectorAndNumber>CollectorAndNumber</CollectorAndNumber>
        <Institution>Institution</Institution>
        <Distribution>Distribution</Distribution>
        <Note></Note>
        <OtherStuff>Unimportant</OtherStuff>
    </record>
</root>
Locality,CollectorAndNumber,Institution,Distribution
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>

    <xsl:param name="porderedNames"
     select="' CollectorAndNumber Locality Distribution Institution Note '"/>

    <xsl:template match="/*/*">
        <xsl:for-each select=
         "*[contains($porderedNames, concat(' ',name(), ' '))]">

         <xsl:sort data-type="number"
          select="string-length(
                     substring-before($porderedNames,
                                      concat(' ',name(), ' ')
                                      )
                                )"/>

            <xsl:value-of select="."/>
            <xsl:if test="not(position() = last())">,</xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>
CollectorAndNumber,Locality,Distribution,Institution
<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:template match="/*/*">
    <xsl:value-of separator="," select=
    "(Locality, CollectorAndNumber,
     Institution, Distribution,
     Note)[text()]" />
    </xsl:template>
</xsl:stylesheet>
Locality,CollectorAndNumber,Institution,Distribution
<!-- Since position() doesn't work as expected(returning node position of current 
node list), I got round it by a string variable and tokenizing it in which 
absolute position is equal to relative(context) position. -->
<xsl:variable name="measObjLdns" >
    <xsl:for-each select="h:measValue[@measObjLdn=$currentMeasObjLdn]/h:measResults" >
        <xsl:value-of select="concat(.,'---')"/> <!-- is an optional separator. -->
    </xsl:for-each>
</xsl:variable>

<xsl:for-each select="str:tokenize($measObjLdns,'---')" ><!-- Since position() doesn't 

work as expected(returning node position of current node list), 
I got round it by a string variable and tokenizing it in which
absolute position is equal to relative(context) position. -->

    <xsl:value-of select="."></xsl:value-of>
    <xsl:if test="position() != last()">
        <xsl:text>,</xsl:text>
    </xsl:if>
</xsl:for-each>
<xsl:if test="position() != last()">
    <xsl:text>,</xsl:text>
</xsl:if>