此后是对question posted here last year的跟进。 仍然是一个新手,我正在努力(再次......)转换 - 使用XSLT 1.0 - 以下描述对象的XML(注意输入中的微小变化 - 'BC *' - 前一个问题):
<Data>
<Object>
<Property Name="Id" Value="001"/>
<Property Name="P.Id" Value="Id P"/>
<Property Name="P.Description" Value="Descr P"/>
<Property Name="A.Id" Value="Id A" />
<Property Name="A.Description" Value="Descr A"/>
<Property Name="B.Id" Value="Id B"/>
<Property Name="B.Description" Value="Descr B"/>
<Property Name="B.C.Id" Value="B.C.Id"/>
<Property Name="B.C.Description" Value="B.C.Description"/>
</Object>
</Data>
以下规则应适用于获得所需的输出:
因此,所需的输出应如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<ObjectData>
<Id>001</Id>
<P>
<Id>Id P</Id>
<Description>Descr P</Description>
</P>
<A>
<Id>Id A</Id>
<Description>Descr A</Description>
</A>
<B>
<Id>Id B</Id>
<Description>Descr B</Description>
<C>
<Id>B.C.Id</Id>
<Description>B.C.Description</C.Description>
</C>
</B>
</ObjectData>
</Root>
目前我有以下代码:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="kPropertyByName" match="Property[contains(@Name, '.')]" use="concat(generate-id(..), '|', substring-before(@Name,'.'))"/>
<xsl:template match="Data">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>
<xsl:template match="Object">
<ObjectData>
<xsl:apply-templates select="Property[not(contains(@Name, '.'))]"/>
<xsl:for-each select="Property[generate-id(.) =
generate-id(key('kPropertyByName',
concat(generate-id(..), '|', substring-before(@Name,'.')))[1])
] ">
<xsl:apply-templates select="." mode="parent"/>
</xsl:for-each>
</ObjectData>
</xsl:template>
<xsl:template match="Property[not(contains(@Name, '.'))]">
<xsl:element name="{@Name}">
<xsl:value-of select="@Value"/>
</xsl:element>
</xsl:template>
<xsl:template match="Property[(contains(@Name, '.'))]" mode="parent">
<xsl:element name="{substring-before(@Name,'.')}">
<xsl:apply-templates mode="child" select="../Property[
substring-before(current()/@Name,'.') =
substring-before(./@Name,'.')]"/>
</xsl:element>
</xsl:template>
<xsl:template match="Property[(contains(@Name, '.'))]" mode="child">
<xsl:element name="{substring-after(@Name,'.')}">
<xsl:value-of select="@Value"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
这给了我以下输出 - 有(不需要的)'非分离'C。* - 元素:
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<ObjectData>
<Id>001</Id>
<P>
<Id>Id P</Id>
<Description>Descr P</Description>
</P>
<A>
<Id>Id A</Id>
<Description>Descr A</Description>
</A>
<B>
<Id>Id B</Id>
<Description>Descr B</Description>
<C.Id>B.C.Id</C.Id>
<C.Description>B.C.Description</C.Description>
</B>
</ObjectData>
</Root>
不是我正在寻找的......任何帮助都会再次受到赞赏!
答案 0 :(得分:1)
有趣的问题,但我只有时间用XSLT 2.0来解决它:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:function name="mf:group" as="element()*">
<xsl:param name="elements" as="element()*"/>
<xsl:param name="index" as="xs:integer"/>
<xsl:for-each-group select="$elements" group-adjacent="tokenize(@Name, '\.')[$index]">
<xsl:choose>
<xsl:when test="not(current-group()[2])">
<xsl:apply-templates select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{current-grouping-key()}">
<xsl:apply-templates select="current-group()[$index = count(tokenize(@Name, '\.'))]"/>
<xsl:sequence select="mf:group(current-group()[not($index = count(tokenize(@Name, '\.')))], $index + 1)"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="Object">
<xsl:copy>
<xsl:sequence select="mf:group(*, 1)"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Property[@Name]">
<xsl:element name="{tokenize(@Name, '\.')[last()]}">
<xsl:value-of select="@Value"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:0)
如果您很幸运,并且您的处理器支持以下EXSLT功能:
然后这可能适合你:
XSLT 1.0 + EXSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:set="http://exslt.org/sets"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl set str">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/Data">
<Root>
<xsl:apply-templates select="Object"/>
</Root>
</xsl:template>
<xsl:template match="Object">
<ObjectData>
<xsl:variable name="nodes">
<xsl:for-each select="Property">
<xsl:variable name="value" select="@Value" />
<xsl:for-each select="str:tokenize(@Name, '.')">
<node parent="{preceding-sibling::*[1]}">
<xsl:if test="position()=last()">
<xsl:attribute name="value">
<xsl:value-of select="$value"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="." />
</node>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="nodes-set" select="exsl:node-set($nodes)/node" />
<xsl:variable name="ancestors" select="set:distinct($nodes-set[not(string(@parent))])" />
<xsl:apply-templates select="$ancestors">
<xsl:with-param name="nodes-set" select="$nodes-set"/>
</xsl:apply-templates>
</ObjectData>
</xsl:template>
<xsl:template match="node">
<xsl:param name="nodes-set"/>
<xsl:element name="{.}">
<xsl:variable name="children" select="set:distinct($nodes-set[@parent=current()])" />
<xsl:apply-templates select="@value"/>
<xsl:apply-templates select="$children">
<xsl:with-param name="nodes-set" select="$nodes-set"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="@value">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
结果(使用libxslt测试时):
<?xml version="1.0" encoding="utf-8"?>
<Root>
<ObjectData>
<Id>001</Id>
<P>
<Id>Id P</Id>
<Description>Descr P</Description>
</P>
<A>
<Id>Id A</Id>
<Description>Descr A</Description>
</A>
<B>
<Id>Id B</Id>
<Description>Descr B</Description>
<C>
<Id>B.C.Id</Id>
<Description>B.C.Description</Description>
</C>
</B>
</ObjectData>
</Root>