需要帮助使用XSLT形成目标xml

时间:2017-11-03 05:25:04

标签: xslt

使用XSLT向父节点创建子元素时遇到困难,因为来自外部源的输入消息的格式有点不同。请求您对XSLT代码提供帮助。如果我可以优化下面复制的现有xslt,也请告诉我。 来自外部来源的输入消息:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<PurchaseOrder id="aoi00037607">
    <attr attr-name="A">
        <new-value>adi00010210</new-value>
    </attr>
    <attr attr-name="B">
        <new-value>99</new-value>
    </attr>
    <attr attr-name="C">
        <new-value>active</new-value>
    </attr>
    <attr attr-name="D">
        <new-value>
            <child1>iop00010538</child1>
            <child2>2</child2>
        </new-value>
        <new-value>
            <child1>cid2313213</child1>
            <child2>2</child2>
        </new-value>
        <new-value>
            <child1>hri00075562</child1>
            <child2>1</child2>
        </new-value>
    </attr>
    <attr attr-name="E">
        <new-value>
            <child3>spi00010021</child3>
            <child4>1</child4>
        </new-value>
        <new-value>
            <child3>vuh000123</child3>
            <child4>1</child4>
        </new-value>
    </attr>
</PurchaseOrder>

为转换而编写的XSLT代码XSLT代码还包括使用|分隔值如果值来自同一来源多次,则为符号。

<?xml version="1.0" encoding="UTF-8"?>
<ns0:stylesheet version="2.0" xmlns:ns0="http://www.w3.org/1999/XSL/Transform">
   <ns0:template match="/">
      <ns1:PurchaseOrderMSG xmlns:ns1="urn:demo:PurchaseOrder">
         <Orders>
            <Order>
               <ns0:attribute name="id" xmlns:ns0="http://www.w3.org/1999/XSL/Transform">
                  <ns0:value-of select="/*/@id"/>
               </ns0:attribute>
               <ns0:call-template name="copy_attr" xmlns:ns0="http://www.w3.org/1999/XSL/Transform">
                  <ns0:with-param name="Attr_value">A</ns0:with-param>
                  <ns0:with-param name="New_Attr">A</ns0:with-param>
               </ns0:call-template>
               <ns0:call-template name="copy_attr" xmlns:ns0="http://www.w3.org/1999/XSL/Transform">
                  <ns0:with-param name="Attr_value">B</ns0:with-param>
                  <ns0:with-param name="New_Attr">B</ns0:with-param>
               </ns0:call-template>
               <ns0:call-template name="copy_attr" xmlns:ns0="http://www.w3.org/1999/XSL/Transform">
                  <ns0:with-param name="Attr_value">C</ns0:with-param>
                  <ns0:with-param name="New_Attr">C</ns0:with-param>
               </ns0:call-template>
            </Order>
         </Orders>
      </ns1:PurchaseOrderMSG>
   </ns0:template>
   <ns0:template name="copy_attr">
      <ns0:param name="Attr_value"/>
      <ns0:param name="New_Attr" select="$Attr_value"/>
      <ns0:param name="length" select="100000"/>
      <ns0:param name="values">
         <ns0:for-each select="//attr[@attr-name = $Attr_value]/new-value">
            <ns0:if test="position()!=1">
               <ns0:text>|</ns0:text>
            </ns0:if>
            <ns0:value-of select="."/>
         </ns0:for-each>
      </ns0:param>
      <ns0:element name="{$New_Attr}">
         <ns0:value-of select="substring($values,1,number($length))"/>
      </ns0:element>
   </ns0:template>
</ns0:stylesheet>

我面临着难以编写的代码,将child1 / child2(两个孩子都可以重复多次)字段添加到parentnode D(出现0到无界)和child3 / child4到parentnode E.

预期结果:

<?xml version="1.0" encoding="UTF-8"?>
<ns0:PurchaseOrderMSG xmlns:ns0="urn:demo:PurchaseOrder">
   <Orders>
      <Order>
         <A/>
         <B/>
         <C/>
         <D>
            <Child1/>
            <Child2/>
         </D>
         <E>
            <Child3/>
            <Child4/>
         </E>
      </Order>
   </Orders>
</ns0:PurchaseOrderMSG>
please find the changed input..I have retained old input as well
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<PurchaseOrder id="aoi00037607">
    <attr attr-name="A">
        <new-value>adi00010210</new-value>
    </attr>
    <attr attr-name="B">
        <new-value>99</new-value>
    </attr>
        <attr attr-name="B">
        <new-value>101</new-value>
    </attr>
    <attr attr-name="C">
        <new-value>active</new-value>
    </attr>
    <attr attr-name="D">
        <new-value>
            <child1>iop00010538</child1>
            <child2>2</child2>
        </new-value>
        <new-value>
            <child1>cid2313213</child1>
            <child2>2</child2>
        </new-value>
        <new-value>
            <child1>hri00075562</child1>
            <child2>1</child2>
        </new-value>
    </attr>
    <attr attr-name="E">
        <new-value>
            <child3>spi00010021</child3>
            <child4>1</child4>
        </new-value>
        <new-value>
            <child3>vuh000123</child3>
            <child4>1</child4>
        </new-value>
    </attr>
    <attr attr-name="C">
        <new-value>inactive</new-value>
    </attr>
</PurchaseOrder>
Please find the expected output
<?xml version="1.0" encoding="UTF-8"?>
<ns0:PurchaseOrderMSG xmlns:ns0="urn:demo:PurchaseOrder">
   <Orders>
      <Order>
         <A>adi00010210</A>
         <B>99|101</B>
         <C>active|inactive</C>
         <D>
            <Child1>iop00010538</Child1>
            <Child2>2</Child2>
         </D>
         <D>
            <child1>cid2313213</child1>
            <child2>2</child2>
        </D>
        <D>
            <child1>hri00075562</child1>
            <child2>1</child2>
         </D>
         <E>
            <Child3>spi00010021</Child3>
            <Child4>1</Child4>
         </E>
         <E>
            <child3>vuh000123</child3>
            <child4>1</child4>
         </E>
      </Order>
   </Orders>
</ns0:PurchaseOrderMSG>

1 个答案:

答案 0 :(得分:1)

在我们进入解决方案之前,我想建议保留名称空间http://www.w3.org/1999/XSL/Transform的前缀为xsl而不是ns0,因为它被广泛接受并且更容易理解。但它不是强制性的,也不是个人选择。下面的解决方案使用xsl作为前缀。

首先,我们需要根据attr-name元素的<attr>值来准备节点列表。这可以通过在<xsl:for-each>元素上使用<attr>并使用attribute value templates {}作为元素名称来实现。

<xsl:for-each select="attr">
    <xsl:element name="{@attr-name}">
    ....
    </xsl:element>
</xsl:for-each>

接下来是对父节点的值进行分组,并使用|分隔符将它们分开。这可以通过在<xsl:key>中定义XSLT 1.0来实现。

 <xsl:key name="keyAttrName" match="attr" use="@attr-name" />

如果使用XSLT 2.0,则可以使用<xsl:for-each-group>

<xsl:for-each-group select="attr" group-by="@attr-name">

XSLT 1.0 解决方案

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="urn:demo:PurchaseOrder">
    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*" />

    <xsl:key name="keyAttrName" match="attr" use="@attr-name" />

    <xsl:template match="PurchaseOrder">
        <ns1:PurchaseOrderMSG>
            <Orders>
                <Order id="{@id}">
                    <xsl:for-each select="attr[generate-id() = generate-id(key('keyAttrName', @attr-name)[1])]">
                        <xsl:variable name="nodeName" select="@attr-name" />
                        <xsl:choose>
                            <xsl:when test="key('keyAttrName', @attr-name)/new-value/*/node()">
                                <xsl:for-each select="new-value">
                                    <xsl:element name="{$nodeName}">
                                        <xsl:copy-of select="*" />
                                    </xsl:element>
                                </xsl:for-each>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:element name="{$nodeName}">
                                    <xsl:for-each select="key('keyAttrName', @attr-name)">
                                        <xsl:value-of select="new-value" />
                                            <xsl:if test="position() != last()">
                                            <xsl:value-of select="'|'" />
                                        </xsl:if>
                                    </xsl:for-each>
                                </xsl:element>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:for-each>
                </Order>
            </Orders>
        </ns1:PurchaseOrderMSG>
    </xsl:template>
</xsl:stylesheet>

XSLT 2.0 解决方案

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

    <xsl:template match="PurchaseOrder">
        <ns1:PurchaseOrderMSG>
            <Orders>
                <Order id="{@id}">
                    <xsl:for-each-group select="attr" group-by="@attr-name">
                        <xsl:choose>
                            <xsl:when test="current-group()/new-value/*/node()">
                                <xsl:for-each select="current-group()/new-value">
                                    <xsl:element name="{current-grouping-key()}">
                                        <xsl:copy-of select="*" />
                                    </xsl:element>
                                </xsl:for-each>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:element name="{current-grouping-key()}">
                                    <xsl:value-of select="current-group()/new-value" separator="|" />
                                </xsl:element>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:for-each-group>
                </Order>
            </Orders>
        </ns1:PurchaseOrderMSG>
    </xsl:template>
</xsl:stylesheet>

两种解决方案都会在下面显示的输出中转换更新的输入XML。

<ns1:PurchaseOrderMSG xmlns:ns1="urn:demo:PurchaseOrder">
   <Orders>
      <Order id="aoi00037607">
         <A>adi00010210</A>
         <B>99|101</B>
         <C>active|inactive</C>
         <D>
            <child1>iop00010538</child1>
            <child2>2</child2>
         </D>
         <D>
            <child1>cid2313213</child1>
            <child2>2</child2>
         </D>
         <D>
            <child1>hri00075562</child1>
            <child2>1</child2>
         </D>
         <E>
            <child3>spi00010021</child3>
            <child4>1</child4>
         </E>
         <E>
            <child3>vuh000123</child3>
            <child4>1</child4>
         </E>
      </Order>
   </Orders>
</ns1:PurchaseOrderMSG>