如何移除XSLT输出中的xmlns:xsi命名空间声明?
如何移除XSLT转换输出中的xmlns:xsi声明
问题场景
XSLT转换后,输出的子元素上自动带上了xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"声明,需要将其移除。
输入XML
<?xml version="1.0" encoding="UTF-8"?> <items xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="items_schema.xsd"> <Skyscraper>Willis Tower</Skyscraper> <Small_fruit>blueberry</Small_fruit> <Big_fruit>watermelon </Big_fruit> </items>
原XSLT代码
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xs" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"> <xsl:template match="/items"> <xsl:variable name="fruit" select="*[contains(local-name(), 'fruit')]" /> <items> <Building> <xsl:copy-of select="* except $fruit"/> </Building> <Food> <xsl:copy-of select="$fruit"/> </Food> </items> </xsl:template> </xsl:stylesheet>
实际输出XML
<items> <Building> <Skyscraper xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Willis Tower</Skyscraper> </Building> <Food> <Small_fruit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">blueberry</Small_fruit> <Big_fruit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">watermelon </Big_fruit> </Food> </items>
期望输出XML
<items> <Building> <Skyscraper>Willis Tower</Skyscraper> </Building> <Food> <Small_fruit>blueberry</Small_fruit> <Big_fruit>watermelon </Big_fruit> </Food> </items>
解决方案
出现这个问题的核心原因是:原XML根节点带有xsi命名空间,<xsl:copy-of>默认会复制节点的命名空间上下文,即使子节点本身没有使用该命名空间,也会被自动带上。
方法一:自定义模板复制节点(兼容XSLT 1.0)
如果你的处理器仅支持XSLT 1.0,可以通过自定义模板递归复制节点,同时排除xsi命名空间及相关属性:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="xs xsi" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0"> <!-- 匹配所有元素节点,复制时排除xsi命名空间 --> <xsl:template match="*"> <xsl:element name="{local-name()}"> <!-- 复制除xsi前缀外的所有属性 --> <xsl:copy-of select="@*[not(namespace-uri() = 'http://www.w3.org/2001/XMLSchema-instance')]"/> <!-- 递归处理子节点和文本 --> <xsl:apply-templates/> </xsl:element> </xsl:template> <!-- 匹配文本节点直接复制 --> <xsl:template match="text()"> <xsl:copy/> </xsl:template> <xsl:template match="/items"> <xsl:variable name="fruit" select="*[contains(local-name(), 'fruit')]" /> <items> <Building> <xsl:apply-templates select="* except $fruit"/> </Building> <Food> <xsl:apply-templates select="$fruit"/> </Food> </items> </xsl:template> </xsl:stylesheet>
方法二:使用copy-namespaces="no"属性(XSLT 2.0+)
如果你的处理器支持XSLT 2.0及以上,直接在<xsl:copy-of>中添加copy-namespaces="no"属性即可,这是最简洁的处理方式:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xs" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"> <xsl:template match="/items"> <xsl:variable name="fruit" select="*[contains(local-name(), 'fruit')]" /> <items> <Building> <xsl:copy-of select="* except $fruit" copy-namespaces="no"/> </Building> <Food> <xsl:copy-of select="$fruit" copy-namespaces="no"/> </Food> </items> </xsl:template> </xsl:stylesheet>
copy-namespaces="no"的作用是复制节点时不复制其继承的命名空间声明,直接避免子节点带上xsi的命名空间。
内容的提问来源于stack exchange,提问作者n718an
相关产品推荐
相关产品推荐

