xml – 根据属性值创建父子元素,并抑制输出中的重复元素
作为XSLT的新手,我正在尝试使用XSLT 1.0转换以下描述对象的
XML:
<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="C.Id" Value="" /> <Property Name="C.Description" Value=""/> </Object> <Object> <Property Name="Id" Value="002"/> <Property Name="P.Id" Value="Id P"/> <Property Name="P.Description" Value="Descr P"/> <Property Name="A.Id" Value="" /> <Property Name="A.Description" Value=""/> <Property Name="B.Id" Value="Id B"/> <Property Name="B.Description" Value="Descr B"/> <Property Name="C.Id" Value="Id C" /> <Property Name="C.Description" Value="Descr C"/> </Object> </Data> 以下规则应适用于获得所需的输出: >对于每个’Property’元素,不包含分隔符’.’在’Name’属性中,将’Name’属性转换为子元素,并选择其’Value’属性的值. > a)在’Name’属性中使用’substring-before’分隔符的父元素,和 >(2)的附加规则: > a)如果要创建的’Name’属性中的’substring-before’存在于预定义的数组中,并且’Value’属性具有值,则将output element-name替换为预定义的元素名称. 因此,所需的输出应如下所示: <?xml version="1.0" encoding="UTF-8"?> <Root> <ObjectData> <Id>001</Id> <P> <Id>Id P</Id> <Description>Descr P</Description> </P> <Destination> <Type>A</Type> <Id>Id A</Id> <Description>Descr A</Description> </Destination> </ObjectData> <ObjectData> <Id>002</Id> <P> <Id>Id P</Id> <Description>Descr P</Description> </P> <Destination> <Type>B</Type> <Id>Id B</Id> <Description>Descr B</Description> </Destination> </ObjectData> </Root> 目前我有以下代码: <?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" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/> <xsl:strip-space elements="*"/> <!-- Define keys --> <xsl:key name="kPropertyByName" match="Property[contains(@Name,'.')]" use="concat(generate-id(..),'|',substring-before(@Name,'.'))"/> <!-- Define variables --> <xsl:variable name="vDestinationArray" select="'A,B,C'" /> <!-- Identity transform --> <xsl:template match="@* | node()" name="Identity"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- Match Data --> <xsl:template match="Data" name="Data"> <xsl:element name="Root"> <xsl:for-each select="Object"> <xsl:element name="ObjectData"> <xsl:call-template name="Object" /> </xsl:element> </xsl:for-each> </xsl:element> </xsl:template> <!-- Match Object --> <xsl:template match="Object" name="Object"> <!-- For each 'Property'-element that does *not* contain separator '.' in 'Name'-attribute,just select value as-is--> <xsl:for-each select="Property[not(contains(@Name,'.'))]"> <xsl:element name="{@Name}"> <xsl:value-of select="@Value"/> </xsl:element> </xsl:for-each> <!-- For each 'Property'-element that *does* contain separator '.' in 'Name'-attribute,create a parent element using substring-before separator--> <xsl:for-each select="Property[generate-id(.) = generate-id(key('kPropertyByName',concat(generate-id(..),'.')))[1])]"> <!-- Determine whether parent exists in 'array'-variable --> <xsl:choose> <!-- Parent *does* exists in 'array'-variable --> <xsl:when test="contains(concat(',',$vDestinationArray,'),concat(','.'),'))"> <xsl:choose> <!-- If value is not empty,create 'Destination'-element --> <xsl:when test="@Value!=''"> <xsl:element name="Destination"> <xsl:element name="Type"> <xsl:value-of select="substring-before(@Name,'.')" /> </xsl:element> <xsl:for-each select="key('kPropertyByName','.')))"> <xsl:element name="{substring-after(@Name,'.')}"> <xsl:value-of select="@Value"/> </xsl:element> </xsl:for-each> </xsl:element> </xsl:when> </xsl:choose> </xsl:when> <!-- Parent does *not* exists in 'array'-variable --> <xsl:otherwise> <!-- Create child element using substring-after separator --> <xsl:element name="{substring-before(@Name,'.')}"> <xsl:for-each select="key('kPropertyByName','.')))"> <xsl:element name="{substring-after(@Name,'.')}"> <xsl:value-of select="@Value"/> </xsl:element> </xsl:for-each> </xsl:element> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:template> </xsl:stylesheet> 这给了我以下输出 – 有(不需要的)重复’Destination’元素: <?xml version="1.0" encoding="UTF-8"?> <Root> <ObjectData> <Id>001</Id> <P> <Id>Id P</Id> <Description>Descr P</Description> </P> <Destination> <Type>A</Type> <Id>Id A</Id> <Description>Descr A</Description> </Destination> <Destination> <Type>B</Type> <Id>Id B</Id> <Description>Descr B</Description> </Destination> </ObjectData> <ObjectData> <Id>002</Id> <P> <Id>Id P</Id> <Description>Descr P</Description> </P> <Destination> <Type>B</Type> <Id>Id B</Id> <Description>Descr B</Description> </Destination> <Destination> <Type>C</Type> <Id>Id C</Id> <Description>Descr C</Description> </Destination> </ObjectData> </Root> 不是我正在寻找…任何帮助将不胜感激! 解决方法
这是一个更短/更简单的(没有xsl:if,没有xsl:key,没有generate-id())解决方案:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my" extension-element-prefixes="my"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <my:names> <n>A</n> <n>B</n> <n>C</n> </my:names> <xsl:template match="*"> <Root><xsl:apply-templates/></Root> </xsl:template> <xsl:template match="/*/*"> <ObjectData><xsl:apply-templates/></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"> <xsl:element name="{substring-before(@Name,'.')}"> <xsl:element name="{substring-after(@Name,'.')}"> <xsl:value-of select="@Value"/> </xsl:element> <xsl:apply-templates mode="descr" select= "../*[@Name = concat(substring-before(current()/@Name,'.','Description')]"/> </xsl:element> </xsl:template> <xsl:template match= "Property[string(@Value) and contains(@Name,'.') and substring-before(@Name,'.') = document('')/*/my:names/*] [1] "> <Destination> <Type><xsl:value-of select="substring-before(@Name,'.')"/></Type> <xsl:element name="{substring-after(@Name,'.')}"> <xsl:value-of select="@Value"/> </xsl:element> <xsl:apply-templates mode="descr" select= "../*[@Name = concat(substring-before(current()/@Name,'Description')]"/> </Destination> </xsl:template> <xsl:template match= "Property[contains(@Name,'.') and substring-before(@Name,'.') = document('')/*/my:names/* and not(string(@Value)) ]"/> <xsl:template match= "Property[contains(@Name,'.') = document('')/*/my:names/* and string(@Value) ][not(position() = 1)]"/> <xsl:template match="*[substring-after(@Name,'.') = 'Description']"/> <xsl:template match="*" mode="descr"> <Description><xsl:apply-templates select="@Value"/></Description> </xsl:template> </xsl:stylesheet> 在提供的XML文档上应用此转换时: <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="C.Id" Value="" /> <Property Name="C.Description" Value=""/> </Object> <Object> <Property Name="Id" Value="002"/> <Property Name="P.Id" Value="Id P"/> <Property Name="P.Description" Value="Descr P"/> <Property Name="A.Id" Value="" /> <Property Name="A.Description" Value=""/> <Property Name="B.Id" Value="Id B"/> <Property Name="B.Description" Value="Descr B"/> <Property Name="C.Id" Value="Id C" /> <Property Name="C.Description" Value="Descr C"/> </Object> </Data> 产生了想要的正确结果: <Root> <ObjectData> <Id>001</Id> <P> <Id>Id P</Id> <Description>Descr P</Description> </P> <Destination> <Type>A</Type> <Id>Id A</Id> <Description>Descr A</Description> </Destination> </ObjectData> <ObjectData> <Id>002</Id> <P> <Id>Id P</Id> <Description>Descr P</Description> </P> <Destination> <Type>B</Type> <Id>Id B</Id> <Description>Descr B</Description> </Destination> </ObjectData> </Root> (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |