【问题标题】:Making XSLT 1.0 create XML with schema in mind使 XSLT 1.0 创建 XML 时考虑到模式
【发布时间】:2013-06-13 15:44:21
【问题描述】:

我正在转换不包含架构中所有元素的 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))。我觉得我离这里很近,但需要一点点推动才能获得所需的输出。

保罗


这里是完整的 XSLT 文件,使用 Michael Kay 的模板进行了修改,但效果不太好:

<?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>

【问题讨论】:

  • (题外话,我做了一个双重考虑:我正在阅读约翰·勒卡雷的“一个微妙的真相”,其中主角伪装成保罗·安德森)。

标签: xml xslt xsd


【解决方案1】:

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

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

【讨论】:

  • 我提供了一个具体的例子来说明我正在尝试做什么以及我有多接近。
【解决方案2】:

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

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

要处理实例中名为 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>

未测试。

【讨论】:

  • 好的,所以它不是真正的模式,是吗?我正在使用 InfoPath 中的 MainDataSource 文件。是的,我也有实际的架构 .xsd 文件。我明天试试你的解决方案。同时,您认为直接从 .xsd 文件工作会更容易吗?保罗安德森(不是冒名顶替者)
  • Michael,您的模板可以很好地从两个 XML 文件中正确抓取元素。但是从数据文件中获取的元素中没有值。具体来说,这一行似乎是问题所在:&lt;xsl:apply-templates select="$EC"/&gt;
  • 最好为没有子元素的元素添加另一个模板规则:&lt;xsl:template match="*[not(*)]"&gt;&lt;xsl:copy-of select="."/&gt;&lt;/xsl:template&gt;
  • 太棒了!我不会想到那种语法。在针对真实数据进行测试时,我遇到了一些重复数据的问题,从而导致架构错误。明天我会进一步研究。
  • 重复数据的问题是由于架构的不同区域中具有相同名称的多个元素。所以检查 name() 是不够的——我需要确定元素的完整 XPath 或更改我们的架构以避免单个元素名称的任何重复。
猜你喜欢
  • 2019-04-08
  • 1970-01-01
  • 1970-01-01
  • 2018-06-24
  • 1970-01-01
  • 1970-01-01
  • 2014-11-30
  • 2016-04-23
  • 1970-01-01
相关资源
最近更新 更多