我是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>
答案 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>