使用通用根和公共标头将XML消息拆分为多个消息

时间:2015-01-21 19:33:05

标签: xml xslt

我是XSLT的初学者,并花了一些时间试图弄清楚如何进行以下转换......

我想将一个包含一个标题和多个项目的XML消息分成多个XML消息,其中每条消息只有一个item元素,而xml的其余部分是相同的。

因此,如果我们查看以下XML输入消息:

<RequestOne xmlns:h="http://www.w3.org/TR/myns/"    xmlns:f="http://www.w3schools.com/furniture"    xmlns:p="http://www.w3.org/p">
<Header>
    <ID>AB1234</ID>
    <Number>61</Number>
</Header>

<Item>
    <Name>Item1</Name>
    <ItemID>I001</ItemID>
</Item>
<Item>
    <Name>Item2</Name>
    <ItemID>I002</ItemID>
</Item>
<Item>
    <Name>Item3</Name>
    <ItemID>I003</ItemID>
</Item>
</RequestOne>

我想将它转换为具有相同结构和元素的3条消息,除了每条消息都有一个原始XML项。

一个重要的要求是XSL代码对任何根元素名称和名称空间都是通用的。也就是说,请求消息的根目录可以是<RequestOne>, <RequestTwo> ... <RequestN>,并且它的所有名称空间也应该被复制。

我设法创建了以下代码。我认为它缺少复制根元素及其名称空间。

<xsl:for-each select="//*[local-name()='Item']"> 
    <!-- Missing here the part that would copy the root element along with the namespaces -->
    <xsl:copy-of select="../Item"/>
    <xsl:copy-of select="." />
</xsl:for-each> 

如果有多种方法可以执行此操作,是否可以仅在一个模板中执行此操作?

更新:

很抱歉没有提供预期的输出。它在下面

消息N:

<RequestN xmlns:h="http://www.w3.org/TR/myns/"  xmlns:f="http://www.w3schools.com/furniture"    xmlns:p="http://www.w3.org/p">
    <Header>
        <ID>AB1234</ID>
        <Number>61</Number>
    </Header>
    <Item>
        <Name>ItemN</Name>
        <ItemID>I00N</ItemID>
    </Item>
</RequestN>

2 个答案:

答案 0 :(得分:1)

以下是否适合您?

XSLT 1.0

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

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

<xsl:template match="*[local-name()='Item']">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="../*[local-name()='Header']/*"/>
        <xsl:apply-templates select="node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="*[local-name()='Header']"/>

</xsl:stylesheet>

应用于您的输入示例,结果为:

<?xml version="1.0" encoding="UTF-8"?>
<RequestOne xmlns:h="http://www.w3.org/TR/myns/" xmlns:f="http://www.w3schools.com/furniture" xmlns:p="http://www.w3.org/p">
   <Item>
      <ID>AB1234</ID>
      <Number>61</Number>
      <Name>Item1</Name>
      <ItemID>I001</ItemID>
   </Item>
   <Item>
      <ID>AB1234</ID>
      <Number>61</Number>
      <Name>Item2</Name>
      <ItemID>I002</ItemID>
   </Item>
   <Item>
      <ID>AB1234</ID>
      <Number>61</Number>
      <Name>Item3</Name>
      <ItemID>I003</ItemID>
   </Item>
</RequestOne>
BTW,源XML中的所有命名空间都未使用(因此是多余的)。如果您的输入始终如此,则可以将样式表简化为:

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

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

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

<xsl:template match="Header"/>

</xsl:stylesheet>
  

如果有多种方法可以做到这一点,是否可以这样做   它只在一个模板中?

你为什么要关心这个?

答案 1 :(得分:1)

根据我的理解,您希望将每个Item复制到单独的Request中,保留Request的名称空间,并省略Header

遵循XSLT

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"  omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
 <xsl:strip-space elements="*"/>
  <xsl:template match="*[starts-with(name(),'Request')]">
     <xsl:for-each select="Item">
      <xsl:apply-templates select="parent::*" mode="message">
        <xsl:with-param name="item" select="."/>
      </xsl:apply-templates>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="*[starts-with(name(),'Request')]" mode="message">
  <xsl:param name="item"/>
     <xsl:copy>
       <xsl:copy-of select="$item"/>
     </xsl:copy>
   </xsl:template>
</xsl:transform>

当应用于您的输入时,XML会生成输出

<RequestOne xmlns:h="http://www.w3.org/TR/myns/" xmlns:f="http://www.w3schools.com/furniture" xmlns:p="http://www.w3.org/p">
  <Item>
    <Name>Item1</Name>
    <ItemID>I001</ItemID>
  </Item>
</RequestOne>
<RequestOne xmlns:h="http://www.w3.org/TR/myns/" xmlns:f="http://www.w3schools.com/furniture" xmlns:p="http://www.w3.org/p">
   <Item>
     <Name>Item2</Name>
     <ItemID>I002</ItemID>
   </Item>
 </RequestOne>
 <RequestOne xmlns:h="http://www.w3.org/TR/myns/" xmlns:f="http://www.w3schools.com/furniture" xmlns:p="http://www.w3.org/p">
   <Item>
     <Name>Item3</Name>
     <ItemID>I003</ItemID>
   </Item>
</RequestOne>

请注意,此输出不是有效的XML。取决于不是绝对清楚的所需输出 - 可能分割的消息只是未知完整输出的一部分 - 可以通过将第一个模板更改为例如,将其更改为有效的XML输出。

<xsl:template match="*[starts-with(name(),'Request')]">
  <Requests>
     <xsl:for-each select="Item">
      <xsl:apply-templates select="parent::*" mode="message">
        <xsl:with-param name="item" select="."/>
      </xsl:apply-templates>
    </xsl:for-each>
  </Requests>
</xsl:template>

以防模板仅应用于单个请求。

如果输入XML中有多个请求,则在添加以下模板时,上面的第一个XSLT会生成有效的XML:

<xsl:template match="/">
  <Requests>
    <xsl:apply-templates select="//*[starts-with(name(),'Request')]"/>
  </Requests>
</xsl:template>

如果您想在输出中保留xml声明,只需从omit-xml-declaration="yes"中删除属性xsl:output

更新:由于在问题中更新了所需的输出 - 要在每个拆分Header中设置Request,可以通过以下调整完成此操作:

<xsl:template match="*[starts-with(name(),'Request')]" mode="message">
<xsl:param name="item"/>
  <xsl:copy>
    <xsl:copy-of select="Header"/>
    <xsl:copy-of select="$item"/>
  </xsl:copy>
</xsl:template>

<xsl:copy-of select="Header"/>只复制每个拆分Header中的Request,例如只有三个中的第一个:

<RequestOne xmlns:h="http://www.w3.org/TR/myns/"
        xmlns:f="http://www.w3schools.com/furniture"
        xmlns:p="http://www.w3.org/p">
  <Header>
    <ID>AB1234</ID>
    <Number>61</Number>
  </Header>
  <Item>
    <Name>Item1</Name>
    <ItemID>I001</ItemID>
  </Item>
</RequestOne>