2009-02-13 75 views
3

我有一个属性,其值可能是一个或多个文本字符串,所有文本字符串都用逗号分隔。 我希望使用XSL将属性值转换为它们自己的元素;如何分解由a分隔的属性,分解为单独的元素

e.g

<post title='Hello World" tags="Test,Hello,World /> 

在我想它转化为;

<post> 
<title>Hello World</title> 
<tag>Test</tag> 
<tag>Hello</tag> 
<tag>World</tag> 
</post> 

这可能吗? TIA

+0

你错配了“和 ”并留下了“ 未闭合。 – Sparr 2009-02-13 02:13:23

回答

5

有几种方法可以做到这一点。

I.在XSLT 1.0 使用递归所谓命名模板这种转化

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

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

    <xsl:template match="@*[not(name()='tags')]"> 
     <xsl:element name="{name()}"> 
     <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:template> 

    <xsl:template match="@tags"> 
     <xsl:call-template name="tokenize"> 
     <xsl:with-param name="pText" 
     select="concat(., ',')"/> 
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="tokenize"> 
     <xsl:param name="pText"/> 

     <xsl:if test="string-length($pText)"> 
     <tag> 
      <xsl:value-of select= 
      "substring-before($pText, ',')"/> 
     </tag> 

     <xsl:call-template name="tokenize"> 
      <xsl:with-param name="pText" select= 
      "substring-after($pText, ',')"/> 
     </xsl:call-template> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

当在最初提供的XML文档施加(校正为良好的形成):

<post title="Hello World" 
     tags="Test,Hello,World" /> 

产生所需的结果

<post> 
    <title>Hello World</title> 
    <tag>Test</tag> 
    <tag>Hello</tag> 
    <tag>World</tag> 
</post> 

二,从FXSL 1.x的

这里使用str-split-to-words模板/功能FXSL提供标记化功能

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:ext="http://exslt.org/common" 
> 

    <xsl:import href="strSplit-to-Words.xsl"/> 

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

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

    <xsl:template match="@*[not(name()='tags')]"> 
     <xsl:element name="{name()}"> 
     <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:template> 

    <xsl:template match="@tags"> 
    <xsl:variable name="vwordNodes"> 
     <xsl:call-template name="str-split-to-words"> 
     <xsl:with-param name="pStr" select="."/> 
     <xsl:with-param name="pDelimiters" 
        select="','"/> 
     </xsl:call-template> 
    </xsl:variable> 

    <xsl:apply-templates select="ext:node-set($vwordNodes)/*"/> 
    </xsl:template> 

    <xsl:template match="word"> 
    <tag> 
     <xsl:value-of select="."/> 
    </tag> 
    </xsl:template> 

</xsl:stylesheet> 

当作为前对相同的XML文档应用中,相同的正确的输出产生

三,使用来自XSLT 2.0转换的XPath 2.0标准函数tokenize()

这是最简单的方法 - 如果可以使用XSLT 2.0处理器。

下面的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:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="@*[not(name()='tags')]"> 
     <xsl:element name="{name()}"> 
     <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:template> 

    <xsl:template match="@tags"> 
    <xsl:for-each select="tokenize(.,',')"> 
     <tag><xsl:value-of select="."/></tag> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

当对相同的XML文档施加再次产生希望的结果。

+0

Dimitre,谢谢你的详细解决方案。已经运行他们与轻微的mods,它的作品一种享受。 XSL 1.0 tho。 谢谢 – 2009-02-13 23:52:50

0

你应该做的第一件事是找到使用属性的人,他应该使用元素并让他停下来。我们接受XML的冗长的原因是,它给了我们无需弄清楚如何解析数据的好处。如果你打算用必须解析的数据打包你的XML,为什么你首先使用XML?

+0

这不是100%正确的有所谓的*列表类型*在XML和XML Schema(如IDREFS),这应该是ID或名称的空格分隔的列表。如果有人使用XPath 2.0/XSLT2.0,那么这种类型的输入并不那么具有挑战性。 – 2009-02-13 21:09:26

3

我真的很喜欢Dimitre的答案,但他的XSLT 1.0解决方案有一个小错误。当我有 “价值,价值,价值”时,它只会将前两个分割并跳过最后一个。

编辑这是不是一个错误,我没有注意到他被调用模板,CONCAT

这是我修改他的记号化XSLT 1个模板。

<xsl:template name="tokenize"> 
    <xsl:param name="pText"/> 
    <xsl:param name="pTag"/> 
    <xsl:if test="string-length($pText)"> 
    <xsl:element name="{$pTag}"> 
      <xsl:choose> 
        <xsl:when test="string-length(substring-before($pText, ','))"> 
         <xsl:value-of select="substring-before($pText, ',')"/> 
        </xsl:when> 
        <xsl:otherwise><xsl:value-of select="$pText" /></xsl:otherwise> 
       </xsl:choose> 
    </xsl:element> 

    <xsl:call-template name="tokenize"> 
     <xsl:with-param name="pText" select= 
     "substring-after($pText, ',')"/> 
     <xsl:with-param name="pTag"><xsl:value-of select="$pTag" /></xsl:with-param> 
    </xsl:call-template> 
    </xsl:if> 

用法示例

<xsl:call-template name="tokenize"> 
     <xsl:with-param name="pText">123,234,345</xsl:with> 
     <xsl:with-param name="pTag">tag</xsl:with-param > 
</xsl:call-template> 

输出

<tag>123</tag> 
    <tag>234</tag> 
    <tag>345</tag> 
+0

它没有错误,因为他用``。 tokenizer递归模板有很多种形式。你的是一些多么详细... – 2011-04-07 17:19:30