通用的XML文档操作
-
19-09-2019 - |
题
我们有一个包含许多类似isProduct,isActive,isMandatory标记节点的节点位置的文字可能是真或假的XML文档。
这是需要来操纵文档,并保持它们的结构,但是上述的节点转换为口头表示象下面这样:
< isProduct >True</ isProduct > ===> <Type>Product<Type>
< isProduct >False</ isProduct > ===> <Type/>
和相同的其他标志节点。
我们正在寻求一种可扩展和可伸缩的解决方案,可以与部署后最小的摩擦来配置。
通过扩展的;我们的意思是将有更多的案件;像2个标志位表示的状态;即isEmployee和isCustomer在文档中用于表示4个不同的命名东西.;因此4个可能的组合应该仅转化为像“雇员”,“客户”,“客户雇员”或“无”一个字符串。
通过在可伸缩;我们的意思是它可以被用于处理任何XML文档而无需其模式的现有的理解和对文档尺寸没有限制。
我们也明白,这可能使用XSLT来完成,我们可以写一个XSLT是一个接受任何文件,并增加了额外的节点产生相同的文档或更新?
解决方案
假设像这样的输入:
<gizmo>
<isProduct>True</isProduct>
<isFoo>False</isFoo>
<isBar>True</isBar>
</gizmo>
通用的做法是:
<xsl:template match="gizmo">
<xsl:copy>
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
<Type>
<xsl:if test=". = 'True'">
<xsl:value-of select="substring-after(local-name(), 'is')" />
</xsl:if>
</Type>
</xsl:template>
哪些生产:
<gizmo>
<Type>Product</Type>
<Type />
<Type>Bar</Type>
</gizmo>
一个更一般化的方法使用(重)改性恒等变换:
<!-- the identity template... well, sort of -->
<xsl:template match="node() | @*">
<xsl:copy>
<!-- all element-type children that begin with 'is' -->
<xsl:variable name="typeNodes" select="
*[substring(local-name(), 1, 2) = 'is']
" />
<!-- all other children (incl. elements that don't begin with 'this ' -->
<xsl:variable name="otherNodes" select="
@* | node()[not(self::*) or self::*[substring(local-name(), 1, 2) != 'is']]
" />
<!-- identity transform all the "other" nodes -->
<xsl:apply-templates select="$otherNodes" />
<!-- collapse all the "type" nodes into a string -->
<xsl:if test="$typeNodes">
<Type>
<xsl:variable name="typeString">
<xsl:apply-templates select="$typeNodes" />
</xsl:variable>
<xsl:value-of select="substring-after($typeString, '-')" />
</Type>
</xsl:if>
</xsl:copy>
</xsl:template>
<!-- this collapses all the "type" nodes into a string -->
<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
<xsl:if test=". = 'True'">
<xsl:text>-</xsl:text>
<xsl:value-of select="substring-after(local-name(), 'is')" />
</xsl:if>
</xsl:template>
<!-- prevent the output of empty text nodes -->
<xsl:template match="text()">
<xsl:if test="normalize-space() != ''">
<xsl:value-of select="." />
</xsl:if>
</xsl:template>
上面的需要任何XML输入任何,并且输出相同的结构,仅命名<is*>
元件被折叠成如短划线分隔的字符串单个<Type>
节点:
<!-- in -->
<foo>
<fancyNode />
<gizmo>
<isProduct>True</isProduct>
<isFoo>False</isFoo>
<isBar>True</isBar>
</gizmo>
</foo>
<!-- out -->
<foo>
<fancyNode />
<gizmo>
<Type>Product-Bar</Type>
</gizmo>
</foo>
其他提示
下面是基于该身份转换在XSLT的溶液:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="isProduct">
<xsl:choose>
<xsl:when test=". = 'True'"><Type>Product</Type></xsl:when>
<xsl:otherwise><Type/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
不隶属于 StackOverflow