XSL无法在XML转换中创建子元素

时间:2017-07-24 10:24:23

标签: c# xml xslt

尝试并尝试让以下内容正常运行。我有一个类似于

的XML文件(序列化的C#类)
<?xml version="1.0" encoding="utf-8"?>
<MyXml xmlns:xsd="http://www.w3.org/2001/XMLSchema"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Order>
  <Header> 
    <OrderNumber>1234</OrderNumber>
  </Header>
  <Line>
    <Sku>abc</Sku>
    <Qty>300</Qty>
  </Line>
  <Line>
    <Sku>xyz</Sku>
    <Qty>19</Qty>
  </Line>
</Order>

我需要将其转换为:

<?xml version="1.0" encoding="utf-8"?>
<Order Number="1234">
  <Line>
    <Product>abc</Product>
    <Quantity>300</Quantity>
  </Line>
  <Line>
    <Product>xyz</Product>
    <Quantity>19</Quantity>
  </Line>
</Order>

使行元素的子行为。

到目前为止,这是我最成功的尝试。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="Header" >
    <Order>
      <xsl:attribute name="Number">
        <xsl:value-of select="OrderNumber"/>
      </xsl:attribute>
      <xsl:apply-templates select="Line"/>    
    </Order>
  </xsl:template>

  <xsl:template  match="Line">
    <Line>
      <Product>
        <xsl:value-of select="Sku" />
      </Product>
      <Quantity>
        <xsl:value-of select="Qty" />
      </Quantity>
    </Line>
  </xsl:template>

</xsl:stylesheet>

使用多个根节点生成下面不正确的xml。

<?xml version="1.0" encoding="utf-8"?>
<Order OrderNumber="1234" />
<Line><Product>abc</Product><Quantity>300</Quantity></Line>
<Line><Product>xyz</Product><Quantity>19</Quantity></Line>

显然我缺少一些东西,但经过几个小时的尝试后,我无法将输出的Line元素视为Order的子元素。有人能指出我正确的方向吗?另外为什么缩进选项似乎只影响输出xml的第一级?

2 个答案:

答案 0 :(得分:1)

您有一个匹配Header的模板,但是在<xsl:apply-templates select="Line"/>之内您需要Line来获取Line元素,但Header不是Line的孩子,所以这什么都不选。

由于XSLT built-in template rules,实际上正在选择输出中的Order元素。您没有匹配Header的模板,因此使用了XSLT的内置模板,该模板将选择{{1}下的LineOrder元素}}

一种解决方案是将匹配Header的模板更改为匹配Order

试试这个XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="Order" >
    <Order Number="{Header/OrderNumber}">
      <xsl:apply-templates select="Line"/>    
    </Order>
  </xsl:template>

  <xsl:template  match="Line">
    <Line>
      <Product>
        <xsl:value-of select="Sku" />
      </Product>
      <Quantity>
        <xsl:value-of select="Qty" />
      </Quantity>
    </Line>
  </xsl:template>
</xsl:stylesheet>

请注意使用Attribute Value Templates来简化NumberOrder属性的创建。

答案 1 :(得分:0)

另一种解决方案是在xsl:apply-templates上使用../Line而不是Line,并在模板上使用mode属性。因此,对模板的更改很少。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="Header" >
    <Order>
      <xsl:attribute name="Number">
        <xsl:value-of select="OrderNumber"/>
      </xsl:attribute>
      <xsl:apply-templates select="../Line" mode="sub"/>  <!-- set 'mode' to 'sub' and add '../' to XPath-->  
    </Order>
  </xsl:template>

  <xsl:template  match="Line" mode="sub">                 <!-- use template only when 'mode' is set to 'sub' -->
    <Line>
      <Product>
        <xsl:value-of select="Sku" />
      </Product>
      <Quantity>
        <xsl:value-of select="Qty" />
      </Quantity>
    </Line>
  </xsl:template>

  <xsl:template match="text()" />      <!-- ignore all unmatched text() nodes -->

</xsl:stylesheet>