将 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>
我是 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>