将 XML 消息拆分为具有通用根和通用 header 的多条消息

Splitting XML message into multiple messages with generic root and common header

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

我想将包含一个 header 和多个项目的 XML 消息分成多个 XML 消息,其中每条消息只有一个项目元素,而其余的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>

以下对你有用吗?

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>

顺便说一句,源 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>

If there is more than one way to do this, would it be possible to do it in only one template?

你为什么要关心这个?

根据我对问题的理解,您想将每个 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 声明,只需从 xsl:output.

中删除属性 omit-xml-declaration="yes"

更新: 由于在问题中更新了所需的输出 - 在每个拆分 Request 中都有 Header,这可以通过以下调整:

<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"/> 只是复制每个拆分的 Request 中的 Header,例如仅复制三个中的第一个:

<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>