使XSLT 1.0在创建XML时考虑了模式

时间:2013-06-17 15:52:22

标签: xml xslt xsd

我正在转换不包含架构中每个元素的XML文件。我应该在XSLT文件中使用什么技术,以便生成的XML包含模式中指定的所有元素?

我知道XSLT 2.0很可能很容易解决这个问题,但我仍然坚持使用XSLT 1.0。


这是我想要做的一个例子:

Schema.xml的

<SAN>
    <STACKMEMBERS>
        <STACKMEMBER>
            <A/>
            <B/>
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <C/>
                    <D/>
                    <E/>
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
    </STACKMEMBERS>
</SAN>

DataFile.xml

<SAN>
    <STACKMEMBERS>
        <STACKMEMBER>
            <A>1111</A>
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <D>3333</D>
                    <E>4444</E>
                </ETHERNETSWITCH>
                <ETHERNETSWITCH>
                    <D>5555</D>
                    <E>6666</E>
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
        <STACKMEMBER>
            <A>2222</A>
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <D>7777</D>
                    <E>8888</E>
                </ETHERNETSWITCH>
                <ETHERNETSWITCH>
                    <D>9999</D>
                    <E>1010</E>
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
    </STACKMEMBERS>
</SAN>

期望输出:

<SAN>
    <STACKMEMBERS>
        <STACKMEMBER>
            <A>1111</A>
            <B />
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <C />
                    <D>3333</D>
                    <E>4444</E>
                </ETHERNETSWITCH>
                <ETHERNETSWITCH>
                    <C />
                    <D>5555</D>
                    <E>6666</E>
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
        <STACKMEMBER>
            <A>2222</A>
            <B />
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <C />               
                    <D>7777</D>
                    <E>8888</E>
                </ETHERNETSWITCH>
                <ETHERNETSWITCH>
                    <C />
                    <D>9999</D>
                    <E>1010</E>
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
    </STACKMEMBERS>
</SAN>

我当前的XSLT文件是......

<?xml version="1.0" encoding="windows-1252"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:variable name="schemaFile" select="document('Schema.xml')"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <SAN>
            <STACKMEMBERS>
                <xsl:for-each select="/SAN/STACKMEMBERS/STACKMEMBER">
                    <xsl:copy-of select="."/>
                    <xsl:copy-of select="$schemaFile/SAN/STACKMEMBERS/STACKMEMBER"/>
                </xsl:for-each>
            </STACKMEMBERS>
        </SAN>
    </xsl:template>

</xsl:stylesheet>

...它给了我错误的结果:

<SAN>
    <STACKMEMBERS>
        <STACKMEMBER>
            <A>1111</A>
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <D>3333</D>
                    <E>4444</E>
                    </ETHERNETSWITCH>
                <ETHERNETSWITCH>
                    <D>5555</D>
                    <E>6666</E>
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
        <STACKMEMBER>
            <A />
            <B />
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <C />
                    <D />
                    <E />
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
        <STACKMEMBER>
            <A>2222</A>
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <D>7777</D>
                    <E>8888</E>
                </ETHERNETSWITCH>
                <ETHERNETSWITCH>
                    <D>9999</D>
                    <E>1010</E>
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
        <STACKMEMBER>
            <A />
            <B />
            <ETHERNETSWITCHES>
                <ETHERNETSWITCH>
                    <C />
                    <D />
                    <E />
                </ETHERNETSWITCH>
            </ETHERNETSWITCHES>
        </STACKMEMBER>
    </STACKMEMBERS>
</SAN>

请注意,元素B和C在架构中定义,但不存在于数据文件中。所需的输出会将模式中包含的元素添加到数据文件中。

选择多个条件会很好(比如select =(one,two))。我觉得我离这里很近,但需要一点点推动才能获得所需的输出。


以下是使用Michael Kay的模板修改的完整XSLT文件,该文件不太有效:

<?xml version="1.0" encoding="windows-1252"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:variable name="Instance" select="/"/>
    <xsl:variable name="Schema" select="document('schema.xml')"/>

    <xsl:template match="*">
        <xsl:copy>
            <xsl:variable name="E" select="."/>
            <xsl:variable name="S" select="$Schema//*[name(.)=name($E)]"/>
            <xsl:for-each select="$S/*">
                <xsl:variable name="SC" select="."/>
                <xsl:variable name="EC" select="$E/*[name(.)=name($SC)]"/>
                <xsl:choose>
                    <xsl:when test="$EC">
                        <xsl:apply-templates select="$EC"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="$SC"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

我写了一个应该返回元素的XPath的模板。然后我修改了上面的模板,通过XPath而不是元素名称来查找元素。结果是堆栈溢出,我不知道我哪里出错了。

以下是无效的XSLT代码。任何帮助将不胜感激。

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2011-03-16T10:53:27">

    <xsl:output indent="yes"/>
        <xsl:strip-space elements="*"/>

        <xsl:variable name="Data" select="/"/>
        <xsl:variable name="Schema" select="document('MainDataSource.xml')"/>

    <xsl:template match="*[not(*)]">
        <xsl:copy-of select="."/>
    </xsl:template>

        <xsl:template name="GetXPath">
                <xsl:param name="element"/>
                <xsl:if test="not(*)">
                        <xsl:apply-templates select="ancestor-or-self::*" mode="path"/>
                </xsl:if>
        </xsl:template>

        <xsl:template match="*" mode="path">
                <xsl:value-of select="concat('/',name())"/>
        </xsl:template>

        <xsl:template match="*">
                <xsl:copy>
                        <xsl:variable name="DataElement" select="."/>

                        <xsl:variable name="DataElementXPath">
                                <xsl:call-template name="GetXPath">
                                        <xsl:with-param name="element" select="$DataElement"/>
                                </xsl:call-template>
                        </xsl:variable>

                        <xsl:variable name="SchemaElement" select="$Schema/*[$DataElementXPath]"/>

                        <xsl:for-each select="$SchemaElement/*">
                                <xsl:variable name="SchemaChild" select="."/>

                                <xsl:variable name="SchemaChildXPath">
                                        <xsl:call-template name="GetXPath">
                                                <xsl:with-param name="element" select="$SchemaChild"/>
                                        </xsl:call-template>
                                </xsl:variable>

                                <xsl:variable name="DataChild" select="$Data/*[$SchemaChildXPath]"/>

                                <xsl:choose>
                                        <xsl:when test="$DataChild">
                                                <xsl:apply-templates select="$DataChild"/>
                                        </xsl:when>
                                        <xsl:otherwise>
                                                <xsl:copy-of select="$SchemaChild"/>
                                        </xsl:otherwise>
                                </xsl:choose>
                        </xsl:for-each>
                </xsl:copy>
        </xsl:template>

</xsl:stylesheet>

2 个答案:

答案 0 :(得分:0)

没有简单的答案。只需编写一个输出有效XML的转换。

周围有映射工具,例如来自Altova,尝试自动完成创建从模式A转换为模式B的样式表的任务。我从未发现它们非常有用,但它们可能适合您。

答案 1 :(得分:0)

右。嗯,很高兴知道你的模式是用你自己发明的专有模式语言编写的。我想我们必须猜测语义,但它们看起来很简单。由于这种模式语言似乎只能表达序列,而不是选择或迭代,因此问题可能比使用更传统的模式语言更简单。

在我看来,您想要应用的规则是:

要在实例中处理名为N的元素E,请在架构中找到名为N的元素S.对于该模式元素的每个子SC,如果E具有相同名称的子EC,则处理EC,否则复制SC。

这意味着:

<xsl:variable name="Instance" select="/"/>
<xsl:variable name="Schema" select="doc('schema.xml')"/>
<xsl:template match="*">
  <xsl:copy>
    <xsl:variable name="E" select="."/>
    <xsl:variable name="S" select="$Schema//*[name(.)=name($E)]"/>
    <xsl:for-each select="$S/*">
      <xsl:variable name="SC" select="."/>
      <xsl:variable name="EC" select="$E/*[name(.)=name($SC)]"/>
      <xsl:choose>
        <xsl:when test="$EC">
          <xsl:apply-templates select="$EC"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy-of select="$SC"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:copy>
</xsl:template>

未经测试。