【问题标题】:XSL-T Adding Missing schema elements from input xml as empty tagsXSL-T 将输入 xml 中缺少的模式元素添加为空标签
【发布时间】:2015-04-19 08:22:23
【问题描述】:

我对 XSL-T 比较陌生。 我的要求很简单。我想将 xml 中不存在的 Schema 的缺失元素添加为空标签。

例如,

<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,我希望将元素 &lt;XMLTagOne&gt;&lt;RepeatableElemenTwo&gt; 添加为空标签。

预期 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>

通过一些初步研究,我发现我必须使用与所有元素匹配的标识模板遍历每个节点。 你能建议我如何解决这个问题吗? 谢谢。

编辑

我的设计方法:

  1. 根据 xsd 创建中间 xml 文档。

类似的,

<Root>
<a></a>
<b></b>
<Root>
  • 遍历所有单个节点。 (Identity Template??)
  • 从我手头的源 XML 中获取每个节点的值。

这种方法的问题

  • 重复元素。
  • 必须检查计数是否 > 1。如果是,则使用 &lt;xsl:for-each&gt; 处理源文档中的节点。

【问题讨论】:

  • 您希望 XSLT 代码解析模式,然后根据模式添加元素吗?或者您是否知道在编写 XSLT 时缺少某些元素而只需要 XSLT 来添加这些特定元素?
  • 在这种情况下,说明需求很容易,将需求转化为设计很难,而将设计转化为代码(可能)很容易。我建议您专注于第二阶段:具体来说,定义一个满足您要求的算法,而不用担心如何在 XSLT 中实现该算法。
  • 嗨,马丁。是否可以将源 XML 文档与从模式中创建的格式化文档(包含所有元素,即空标签)进行比较,并将所有缺少的元素添加到源中?我是新手,如有错误请指正。
  • @michael 我已经用可能的设计解决方案编辑了我的问题。请查看并分享您宝贵的 cmets。
  • Related question (仍然没有答案),您创建“中间 xml 文件”的方法实际上是等效的。

标签: xml xslt xslt-1.0


【解决方案1】:

让我们假设您有一个您所谓的基于 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 中存在或缺失;我们使用 specimen XML 创建它们,并使用 输入 XML 的值(如果存在)填充
  • 检查元素有点棘手,因为它们可能会被重复(因此对于样本 XML 中的每个元素,我们必须遍历输入 XML 中的相应元素)
  • 我的工作假设是没有混合内容,所以每个元素要么包含文本节点,要么包含其他元素;这允许我们只复制输入文件中的文本节点

关于如何获取样本XML:

  • 在最一般的情况下,XML Schema 可以定义具有无限嵌套级别的嵌套结构(例如,在 xhtml 中嵌套 &lt;div&gt; 元素)
  • 模式可以包含选择(元素&lt;A&gt; 可以包含&lt;B1&gt;&lt;b2&gt;),这将使创建适当的XML 样本极其困难,如果不是完全不可能的话李>
  • 如果我们将自己限制在具有没有递归类型没有xs:choicexs:all(只有xs:sequence)的XML模式,我认为这是明智的假设,那么 XSLT 转换以生成具有所有可能属性和元素的 XML 应该非常简单

【讨论】:

    【解决方案2】:

    基本上您自己设置的任务是编写一个模式处理器,它不仅可以验证源文档(以任何模式处理器所做的方式),而且如果它们无效,也可以修复它们。我认为您对这项任务的重要性一无所知。一个通用的解决方案将涉及构建一个与模式中每个复杂类型定义所定义的语法相对应的有限状态机,针对使用此有限状态机验证实例,然后在发现与任何路径不匹配的元素时调用修复功能在 FSM 中。如果你限制自己提供缺失的元素,那么当你在状态 S 中遇到元素 E 并且 E 上没有从 S 的转换时,实际上并不难检测到,那么可能存在一个元素 F 确实从S,这会导致您进入在 E 上具有转换的状态 S2。但即使您发现了这一点,您仍然面临更多挑战:您可能必须在 F、G 和 H 之间进行选择,这都是可能的“修复” "元素;您可能会发现需要插入多个元素才能进行修复(这开始涉及一些复杂的图形搜索);一旦找到要插入的元素,就必须构造该元素的一个实例,其内容是有效的。

    这将是一个很好的博士项目。

    【讨论】:

      【解决方案3】:

      恕我直言,您最好的做法是(手动)将所需的元素插入到您的 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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-02
        • 1970-01-01
        相关资源
        最近更新 更多