1

I'm new to XML/XSLT.

I need to transform an xml file containing elements like this:

<person name="John Smith" />
<person name="Mary Ann Smith" />

Into something like this:

<person firstname="John" lastname="Smith" />
<person firstname="Mary Ann" lastname="Smith" />

name attribute can contain a variable number of words, only the last one should go to lastname and the rest goes to firstname.

I'm trying to achieve this using XSLT and xsltproc on linux, but any other simple solution is welcome.

Thanks, Bruno

4

1 回答 1

1

XSLT 1.0(以及因此的 XPath 1.0)在其字符串操作功能方面有些限制。有substring-before和函数允许您在特定模式的第一次substring-after出现之前和之后提取给定字符串的子字符串,但是没有直接的方法在最后一次出现时分割字符串。您必须使用(尾)递归模板

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
  </xsl:template>

  <xsl:template match="person/@name">
    <xsl:call-template name="splitName"/>
  </xsl:template>

  <xsl:template name="splitName">
    <!-- start with nothing in $first and all the words in $rest -->
    <xsl:param name="first" select="''" />
    <xsl:param name="rest" select="." />

    <xsl:choose>
      <!-- if rest contains more than one word -->
      <xsl:when test="substring-after($rest, ' ')">
        <xsl:call-template name="splitName">
          <!-- move the first word of $rest to the end of $first and recurse.
               For the very first word this will add a stray leading space
               to $first, which we will clean up later. -->
          <xsl:with-param name="first" select="concat($first, ' ',
                                               substring-before($rest, ' '))" />
          <xsl:with-param name="rest" select="substring-after($rest, ' ')" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <!-- $rest is now just the last word of the original name, and $first
             contains all the others plus a leading space that we have to
             remove -->
        <xsl:attribute name="firstname">
          <xsl:value-of select="substring($first, 2)" />
        </xsl:attribute>
        <xsl:attribute name="lastname">
          <xsl:value-of select="$rest" />
        </xsl:attribute>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

例子:

$ cat names.xml
<people>
  <person name="John Smith" />
  <person name="Mary Ann Smith" />
</people>
$ xsltproc split-names.xsl names.xml
<?xml version="1.0"?>
<people>
  <person firstname="John" lastname="Smith"/>
  <person firstname="Mary Ann" lastname="Smith"/>
</people>

如果您不想要该<?xml...?>行,请添加

<xsl:output method="xml" omit-xml-declaration="yes" />

到样式表的顶部,紧跟在开始<xsl:stylesheet>标记之后。

于 2013-05-28T10:15:42.653 回答