假设您有一大块XML,其中定义了多个名称空间前缀,其中一些实际上是具有不同前缀的相同名称空间。使用XSLT有一种不太复杂的方法来合并这些前缀,这样你最终只能为每个命名空间添加一个前缀吗?例如选择最短的一个?
示例
<soapenv:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:f="http://api.example.com/Service"
xmlns:foo="http://api.example.com/Service">
<soapenv:Body>
<foo:serviceResponse>
<f:profile id="1">Alice</f:profile>
<f:profile id="2">Bob</f:profile>
</foo:serviceResponse>
</soapenv:Body>
</soapenv:Envelope>
应该变成例如:
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:f="http://api.example.com/Service">
<soap:Body>
<f:serviceResponse>
<f:profile id="1">Alice</f:profile>
<f:profile id="2">Bob</f:profile>
</f:serviceResponse>
</soap:Body>
</soap:Envelope>
答案 0 :(得分:0)
您可以尝试
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="namespaces" match="dec" use="@ns"/>
<xsl:variable name="prefs">
<xsl:for-each-group select="/*/namespace::*" group-by="string()">
<xsl:for-each select="current-group()">
<xsl:sort select="string-length(local-name())"/>
<xsl:if test="position() eq 1">
<dec ns="{string()}"><xsl:value-of select="local-name()"/></dec>
</xsl:if>
</xsl:for-each>
</xsl:for-each-group>
</xsl:variable>
<xsl:template match="@*">
<xsl:attribute name="{if (key('namespaces', namespace-uri(), $prefs)) then concat(key('namespaces', namespace-uri(), $prefs), ':') else ''}{local-name()}" namespace="{namespace-uri()}" select="."/>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{if (key('namespaces', namespace-uri(), $prefs)) then concat(key('namespaces', namespace-uri(), $prefs), ':') else ''}{local-name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="@* , node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
但是它只检查根元素上的名称空间声明(当它们在文档中的任何元素上都是全部)时,更重要的是,XML模式或SOAP消息中的属性值可以是QName类型(例如xs:integer
)并且结果文档可能不再有效(例如,您声明了两个前缀x
和xs
,算法会删除xs
,但属性值不固定为{ {1}})。因此,如果您想使用该XSLT来修复可能使用限定名称作为元素或属性值的SOAP / XML模式(并且不仅仅是XSLT修复/简化的元素或属性名称),请注意。
答案 1 :(得分:0)
你没有太多的控制权 - 这主要取决于你的处理器的心血来潮。例如,将身份转换模板应用于您的输入将导致:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:f="http://api.example.com/Service" xmlns:foo="http://api.example.com/Service">
<soapenv:Body>
<foo:serviceResponse>
<foo:profile id="1">Alice</foo:profile>
<foo:profile id="2">Bob</foo:profile>
</foo:serviceResponse>
</soapenv:Body>
</soapenv:Envelope>
使用libxslt时,但Saxon将返回:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:f="http://api.example.com/Service" xmlns:foo="http://api.example.com/Service">
<soapenv:Body>
<foo:serviceResponse>
<f:profile id="1">Alice</f:profile>
<f:profile id="2">Bob</f:profile>
</foo:serviceResponse>
</soapenv:Body>
</soapenv:Envelope>
您可以尝试使用以下内容完全剥离前缀:
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
在这里,撒克逊将回归:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<serviceResponse xmlns="http://api.example.com/Service">
<profile id="1">Alice</profile>
<profile id="2">Bob</profile>
</serviceResponse>
</Body>
</Envelope>
而libxslt会生成一些随机(但统一)的前缀,例如
<?xml version="1.0" encoding="UTF-8"?>
<ns13:Envelope xmlns:ns13="http://schemas.xmlsoap.org/soap/envelope/">
<ns13:Body>
<ns14:serviceResponse xmlns:ns14="http://api.example.com/Service">
<ns14:profile id="1">Alice</ns14:profile>
<ns14:profile id="2">Bob</ns14:profile>
</ns14:serviceResponse>
</ns13:Body>
</ns13:Envelope>
并且它们都没有错。
答案 2 :(得分:0)
有趣的问题。这接近你所需要的。但是没有花很多时间在上面,所以可能有办法大幅缩短它。
exclude-result-prefixes
的值。后者可以在下面的转换后修复,使用简单的标识转换来排除不再使用的前缀。例如,请参阅Dimitre Novatchev的回答here。
<强>样式表强>
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:f="http://api.example.com/Service"
xmlns:foo="http://api.example.com/Service">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:variable name="node" select="."/>
<xsl:variable name="prefix" select="substring-before(name(), concat(':', local-name()))"/>
<xsl:variable name="scope">
<xsl:for-each select="in-scope-prefixes(.)">
<xsl:element name="{.}">
<xsl:value-of select="namespace-uri-for-prefix(.,$node)"/>
</xsl:element>
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="$scope/*[name() != $prefix and namespace-uri-for-prefix($prefix,$node) = . and string-length(./name()) lt string-length($prefix)]">
<xsl:variable name="match" select="$scope/*[name() != $prefix and namespace-uri-for-prefix($prefix,$node) = .]"/>
<xsl:element name="{concat($match/name(),':',$node/local-name())}">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="@*|processing-instruction()|text()|comment()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
<强>输出强>
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<f:serviceResponse xmlns:f="http://api.example.com/Service">
<f:profile xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:foo="http://api.example.com/Service"
id="1">Alice</f:profile>
<f:profile xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:foo="http://api.example.com/Service"
id="2">Bob</f:profile>
</f:serviceResponse>
</soap:Body>
</soap:Envelope>
输出(在应用与this类似的内容后)
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<f:serviceResponse xmlns:f="http://api.example.com/Service">
<f:profile id="1">Alice</f:profile>
<f:profile id="2">Bob</f:profile>
</f:serviceResponse>
</soap:Body>
</soap:Envelope>
答案 3 :(得分:0)
这是我最终使用的。不确定它与其他答案相比如何,或者它是否具有相同的功能集,但它适用于我的情况,我不记得我是如何得到它的。如果我在这里调整了其中一个答案,或者我在其他地方找到了答案。在任何一种情况下,它都适合我。请评论是否有任何其他内容缺失或更好的内容,并对其进行投票:)
<stylesheet version="2.0" xmlns="http://www.w3.org/1999/XSL/Transform">
<!--
Pulls namespace declarations up towards root node.
-->
<template match="@* | text() | processing-instruction() | comment()">
<copy />
</template>
<template match="*">
<copy copy-namespaces="no">
<for-each-group group-by="local-name()" select="descendant-or-self::*/namespace::*">
<copy-of select="." />
</for-each-group>
<apply-templates select="@* , node()"/>
</copy>
</template>
</stylesheet>
注意 :不要认为这实际上合并了前缀(不记得了,现在无法测试它),但是我用它了在返回SOAP消息之前的ESB进程结束时,名称空间被清理了很多,所以它至少做了一些事情:p