我对XSL-T还比较陌生。我的要求很简单。我想添加Schema中缺少的元素,这些元素在xml中没有作为空标记出现。
例如,
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified"
elementFormDefault="qualified">
<xs:element name="RootElement">
<xs:complexType>
<xs:sequence>
<xs:element name="XMLTagOne">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="value1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="RepeatableElementOne" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="value2"/>
<xs:element name="RepeatableElemenTwo" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="value3"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
考虑此i/p:
<RootElement>
<RepeatableElementOne>
<value2>bb</value2>
<RepeatableElemenTwo>
<value3>cc</value3>
</RepeatableElemenTwo>
<RepeatableElemenTwo>
<value3>dd</value3>
</RepeatableElemenTwo>
</RepeatableElementOne>
<RepeatableElementOne>
<value2>ee</value2>
</RepeatableElementOne>
</RootElement>
对于这个i/p,我希望元素<XMLTagOne>
和<RepeatableElemenTwo>
作为空标记添加。
预期O/p:
<RootElement>
<XMLTagOne> <!-- Added as empty tag though not present in i/p-->
<value1></value1>
</XMLTagOne>
<RepeatableElementOne>
<value2>bb</value2>
<RepeatableElemenTwo>
<value3>cc</value3>
</RepeatableElemenTwo>
<RepeatableElemenTwo>
<value3>dd</value3>
</RepeatableElemenTwo>
</RepeatableElementOne>
<RepeatableElementOne>
<value2>ee</value2>
<RepeatableElemenTwo>
<value3></value3>
</RepeatableElemenTwo>
</RepeatableElementOne>
</RootElement>
通过一些初步研究,我发现我必须遍历每个节点,并使用与所有元素匹配的身份模板。你能建议我如何处理这个问题吗?谢谢
编辑
我的设计方法:
- 基于xsd创建一个中间xml文档
类似这样的东西,
<Root>
<a></a>
<b></b>
<Root>
- 遍历所有单个节点。(
Identity Template
??) - 从我手头的源XML中获取每个节点的值
此方法中的问题
- 重复元素
- 必须检查计数是否大于1。如果是,则使用
<xsl:for-each>
处理源文档中的节点
让我们假设您有一个基于XSD架构的中间XML文档(最后会详细介绍),从现在开始样本XML:
样本XML:
<RootElement>
<XMLTagOne>
<value1 property=""> </value1>
</XMLTagOne>
<RepeatableElementOne attr1="">
<value2></value2>
<RepeatableElemenTwo>
<value3></value3>
</RepeatableElemenTwo>
</RepeatableElementOne>
</RootElement>
(我添加了几个属性,以表明所提出的解决方案也适用于他们)
输入XML:
<RootElement>
<RepeatableElementOne attr1="lorem ipsum">
<value2>bb</value2>
<RepeatableElemenTwo>
<value3>cc</value3>
</RepeatableElemenTwo>
<RepeatableElemenTwo>
<value3>dd</value3>
</RepeatableElemenTwo>
</RepeatableElementOne>
<RepeatableElementOne>
<value2>ee</value2>
</RepeatableElementOne>
</RootElement>
(根据OP中的输入,添加属性)
XSLT 1.0:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<!--
some node in the input document could be missing,
so we must apply the templates to the specimen document nodes
-->
<xsl:apply-templates select="document('specimen.xml')/*">
<xsl:with-param name="instanceNode" select="*"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="@*">
<xsl:param name="instanceNode"/>
<xsl:attribute name="{name(.)}">
<xsl:value-of select="$instanceNode"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="*">
<xsl:param name="instanceNode"/>
<xsl:choose>
<xsl:when test="$instanceNode">
<!-- the node is present in the input document -->
<xsl:copy>
<!-- attributes -->
<xsl:for-each select="@*">
<xsl:apply-templates select=".">
<xsl:with-param name="instanceNode" select="$instanceNode/@*[name() = name(current())]"/>
</xsl:apply-templates>
</xsl:for-each>
<!-- elements -->
<xsl:for-each select="*">
<xsl:variable name="specimenNode" select="."/>
<xsl:variable name="instanceNodes" select="$instanceNode/*[name() = name(current())]"/>
<xsl:choose>
<xsl:when test="$instanceNodes">
<!-- one or more elements in the input file -->
<xsl:for-each select="$instanceNodes">
<xsl:apply-templates select="$specimenNode">
<xsl:with-param name="instanceNode" select="."/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<!-- missing element in the input file -->
<xsl:apply-templates select="$specimenNode">
<xsl:with-param name="instanceNode" select="''"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!-- text nodes -->
<!-- (working hypotesis: no mixed content) -->
<xsl:value-of select="$instanceNode/text()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<!-- the node is missing in the input document -->
<xsl:copy>
<xsl:apply-templates select="* | @*">
<xsl:with-param name="instanceNode" select="''"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
结果输出:
<RootElement>
<XMLTagOne>
<value1 property=""/>
</XMLTagOne>
<RepeatableElementOne attr1="lorem ipsum">
<value2>bb</value2>
<RepeatableElemenTwo>
<value3>cc</value3>
</RepeatableElemenTwo>
<RepeatableElemenTwo>
<value3>dd</value3>
</RepeatableElemenTwo>
</RepeatableElementOne>
<RepeatableElementOne attr1="">
<value2>ee</value2>
<RepeatableElemenTwo>
<value3/>
</RepeatableElemenTwo>
</RepeatableElementOne>
</RootElement>
注意事项:
- 样式表中的模板应用于样本XML中的节点,因为它们需要为输入文件中缺少的元素执行
- 检查属性是很容易的部分,因为它们在输入XML中要么存在要么缺失;我们使用样本XML创建它们,并用输入XML
- 检查元素有点棘手,因为它们可能会被重复(因此对于样本XML中的每个元素,我们必须遍历输入XML中的相应元素)
- 我的工作假设是有没有混合内容,所以每个元素要么包含文本节点,要么包含其他元素;这允许我们只复制输入文件中的文本节点
关于如何获得样本XML:
- 在最常见的情况下,XML模式可以定义具有无限嵌套级别的嵌套结构(例如,xhtml中的嵌套
<div>
元素) - 模式可以包含选项(元素
<A>
可以包含<B1>
或<b2>
),这将使创建适当的样本XML变得极其困难,甚至完全不可能 - 如果我们将自己限制在没有递归类型和没有
xs:choice
或xs:all
(只有xs:sequence
)的XML模式中,我认为这是一个合理的假设,那么生成具有所有可能属性和元素的XML的XSLT转换应该是非常直接的
本质上,您自己设置的任务是编写一个模式处理器,该处理器不仅验证源文档(就像任何模式处理器一样),而且在它们无效时进行修复。我想你根本不知道这项任务的艰巨性。一般的解决方案将涉及构建与模式中每个复杂类型定义定义的语法相对应的有限状态机,针对使用该有限状态机验证实例,然后在发现与FSM中的任何路径不匹配的元素时调用修复功能。如果你限制自己提供缺失的元素,那么实际上不难检测到,当你在状态s中遇到元素E,并且在E上没有从s转换时,可能有一个元素F确实从s转换,这会导致你进入在E上转换的状态S2。但即使你发现了这一点,你还有几个挑战:你可能必须在F、G和H之间做出选择,这些都是可能的"修复"元素;你可能会发现,你需要插入多个元素来进行修复(这开始涉及一些复杂的图搜索);一旦找到了要插入的元素,就必须为该元素构造一个内容有效的实例。
这将是一个很好的博士项目。
IMHO,您最好的做法是(手动)将所需的元素插入到XSLT样式表中,例如:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/RootElement">
<xsl:copy>
<XMLTagOne>
<value1>
<xsl:value-of select="XMLTagOne/value1"/>
</value1>
</XMLTagOne>
<xsl:apply-templates select="RepeatableElementOne" />
</xsl:copy>
</xsl:template>
<xsl:template match="RepeatableElementOne">
<xsl:copy>
<value2>
<xsl:value-of select="value2"/>
</value2>
<xsl:choose>
<xsl:when test="RepeatableElemenTwo">
<xsl:apply-templates select="RepeatableElemenTwo" />
</xsl:when>
<xsl:otherwise>
<RepeatableElemenTwo>
<value3/>
</RepeatableElemenTwo>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>