用逗号连接两个节点的值

4

我需要转换XML,但是遇到了一些问题...

当前的XML:

<?xml version="1.0" encoding="utf-8"?>
 <Employees>
  <Employee>
   <ManagerFirstName>Joe</ManagerFirstName>
   <ManagerLastName>Schmoe</ManagerLastName>
  </Employee>
 </Employees>

期望的输出结果:

<?xml version="1.0" encoding="utf-8"?>
 <Employees>
  <Employee>
   <supervisorName>Schmoe, Joe</supervisorName>
  </Employee>
 </Employees>

当前的XSL:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" >
  <xsl:template match="/">
    <xsl:apply-templates select="*"/>
  </xsl:template>
  <xsl:template match="node()">
    <xsl:copy><xsl:apply-templates select="node()"/></xsl:copy>
  </xsl:template>
  <xsl:template match="ManagerFirstName">
        <supervisorName>
        <xsl:apply-templates select="node()"/>
        <xsl:value-of  select="/ManagerLastName"/>
        <xsl:text>, </xsl:text>
        <xsl:value-of select="/ManagerFirstName"/>
        </supervisorName>
  </xsl:template>
</xsl:stylesheet>

这个功能目前无法正常工作,我无法找出原因。目前输出的 XML 如下:

<?xml version="1.0" encoding="utf-8"?>
 <Employees>
  <Employee>
   <supervisorName>Joe, </supervisorName>
   <ManagerLastName>Schmoe/ManagerLastName>
  </Employee>
 </Employees>

我感觉我离成功很近...
更新:如何确保如果ManagerFirstName和ManagerLastName为空,那么supervisorName不会有逗号?
更新2:
<?xml version="1.0" encoding="UTF-8" ?>
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes"/> <xsl:strip-space elements="*"/>
 <xsl:template match="/">
   <xsl:apply-templates select="*"/>
 </xsl:template>
 <xsl:template match="@*|node()">
   <xsl:copy>
     <xsl:apply-templates select="@*|node()"/>
   </xsl:copy>
 </xsl:template>
 <xsl:template match="Employee">
   <tbl_EmployeeList><xsl:apply-templates select="@*|node()"/></tbl_EmployeeList>
 </xsl:template>
 <xsl:template match="tbl_EmployeeList">
   <xsl:copy>
     <xsl:apply-templates select="@*|node()"/>
       <supervisorName>
         <xsl:value-of select="(ManagerLastName,ManagerFirstName)" separator=", "/>
       </supervisorName>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

你应该利用XSLT 2.0并在xsl:value-of中使用separator属性。请参考我的回答中的示例。 - Daniel Haley
5个回答

5

由于您正在使用XSLT 2.0,因此可以在 xsl:value-of 中使用 separator 属性...

XML输入

<Employees>
    <Employee>
        <ManagerFirstName>Joe</ManagerFirstName>
        <ManagerLastName>Schmoe</ManagerLastName>
    </Employee>
</Employees>

XSLT 2.0

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

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Employee">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <supervisorName>
                <xsl:value-of select="(ManagerLastName,ManagerFirstName)" separator=", "/>
            </supervisorName>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

XML输出

<Employees>
   <Employee>
      <supervisorName>Schmoe, Joe</supervisorName>
   </Employee>
</Employees>

注意:如果没有ManagerLastNameManagerFirstName,则不会输出分隔符。

再看一遍,我正在使用XML 1.0。不过还是谢谢你的回答! - Josh McKearin
@jpm0004 - 不是XML版本,而是XSLT版本。你的样式表使用了2.0:<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> - Daniel Haley
我尝试了这种方法,因为我需要在没有ManagerLastName或ManagerFirstName时保留分隔符的能力。然而,我遇到了一个错误:期望标记“)”,但找到了“,”。对于问题有什么想法吗? - Josh McKearin
@jpm0004 - 你可以复制一下你正在使用的xsl:value-of吗?另外,你正在使用哪个XSLT处理器? - Daniel Haley
好的,请查看原帖的更新2。我已经修复了我遇到的错误(忘记在XSL中更改根名称),现在问题是我的supervisorName输出为<supervisorName>Joe</supervisorName>。就像它不理解选择器和分隔符一样。至于我使用的处理器,我不确定。它是Visual Studio 2010。 - Josh McKearin
1
@jpm0004 - Visual Studio 默认不支持 XSLT 2.0。您需要使用其他处理器。Saxon 有一个 .NET 实现版本,您可以使用它。http://saxon.sourceforge.net/ 如果您不想使用 XSLT 2.0,您可以在 Ian Roberts 的答案中的第一个 xsl:value-ofxsl:text 周围包装一个 xsl:if。(只需使用 ../ManagerLastName 进行测试即可。) - Daniel Haley

3
您可以按照以下方式完成此操作:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Employee">
        <Employee>
            <supervisorName><xsl:value-of select="concat(ManagerLastName, ', ', ManagerFirstName)"/></supervisorName>
        </Employee>
    </xsl:template>
</xsl:stylesheet>

1
好的解决方案,但最好使用字面序列构造函数而不是xsl:element。此外,您不需要指定text()作为节点到字符串强制转换将自动实现。 - Sean B. Durkin

2
你很接近了。在ManagerFirstName模板中,你需要一个稍微不同的XPath来提取姓氏:
<xsl:template match="ManagerFirstName">
    <supervisorName>
        <xsl:value-of select="../ManagerLastName"/>
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="node()"/>
    </supervisorName>
</xsl:template>

(使用apply-templates足以给出ManagerFirstName的值,您不需要特定的value-of。然后您需要一个无操作模板来阻止它独立复制姓氏。)
<xsl:template match="ManagerLastName" />

请注意,通常的标识模板将匹配并将apply-templates应用于@*|node()而不仅仅是node() - 对于您的示例文档没有使用任何属性,因此没有影响,但如果您的原始XML具有属性,则您的标识模板的版本将删除它们。

这个很好用!但是,我需要在完成工作后删除ManagerLastName节点...那会是什么样子? - Josh McKearin
@jpm0004 这就是 <xsl:template match="ManagerLastName" /> 的用途 - “如果你发现一个 ManagerLastName,就忽略它”。 - Ian Roberts
Ian,还有一件事,因为我只能使用XML 1.0,而你已经解决了这个问题,那么如果ManagerFirstName和ManagerLastName都缺失,我该如何确保不输出任何内容? - Josh McKearin
@jpm0004 XML的版本与此无关。你只需要进行测试就可以确定你所要求的问题。 - Sean B. Durkin
正如DevNull所建议的那样,这种方法与<xsl:if test="../ManagerLastName/text()">结合使用非常好,以便完成我需要的一切。谢谢。 - Josh McKearin
显示剩余2条评论

2

这里是完全的“推送式”解决方案:

<xsl:stylesheet version="1.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="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

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

 <xsl:template match="Employee/*">
  <xsl:value-of select="concat(', ', .)"/>
 </xsl:template>
 <xsl:template match="Employee/*[1]"><xsl:apply-templates/></xsl:template>
</xsl:stylesheet>

1

所有回答都很好,但我认为这种方法更简洁(只需将您的ManagerFirstName模板替换为此模板):

<xsl:template match="Employee">
    <supervisorName>
        <xsl:value-of select="concat(ManagerLastName,', ',ManagerFirstName)"/>
    </supervisorName>
</xsl:template>

更新:

如果您想要仅在两个节点都存在且不为空时显示逗号,您可以使用带有 string-length 的 if 语句来实现:

<xsl:template match="Employee">
    <supervisorName>
        <xsl:value-of select="ManagerLastName"/>
        <xsl:if test="string-length(ManagerLastName) and string-length(ManagerFirstName)">
            <xsl:text> ,</xsl:text>
        </xsl:if>
        <xsl:value-of select="ManagerFirstName"/>
    </supervisorName>
</xsl:template>
更新2: 采用XSLT 2.0方法的更简洁解决方案,同时也涵盖了空节点的情况。
<xsl:template match="Employee">
    <supervisorName>
        <xsl:value-of select="(ManagerLastName[text()],ManagerFirstName[text()])" separator=", "/>
    </supervisorName>
</xsl:template>

这与@LukaszBaran的答案几乎完全相同(除了您交换了名称的顺序)。 - Daniel Haley
你说得对,我想我跳过了那个。无论如何,OP的最后更新改变了请求。 - Carles Sala
你的更新几乎和我的答案一模一样,只是你删除了原始的“Employee”元素。不过这个想法不错。 :-) - Daniel Haley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接