使用XSLT创建针对架构的有效XML

时间:2010-07-08 19:08:17

标签: xml xslt xsd schema xml-validation

我正在根据类型对元素列表进行排序,如我的架构中所定义。我知道XSLT可以针对给定的模式进行验证,但我想要做的是检查以确保我的操作(在这种情况下是副本)在我执行之前是有效的。

简化代码:

传入数据:

<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
    <sch:attr1>val1</sch:attr1>
    <sch:attr2>val2</sch:attr2>
    <sch:attr3>val3</sch:attr3>
    <sch:attr4>val4</sch:attr4>
</sch:foo>

所需的传出数据:

<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
    <sch:bar>
        <sch:attr1>val1</sch:attr1>
        <sch:attr2>val2</sch:attr2>
    </sch:bar>
    <sch:stuff>
        <sch:attr3>val3</sch:attr3>
        <sch:attr4>val4</sch:attr4>
    </sch:stuff>
</sch:fooOut>

模式文件中的某个地方:

<complexType name="fooOut">
    <sequence>
        <!-- ... -->
        <element name="bar">
            <complexType>
                <sequence>
                    <element name="attr1" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                    <element name="attr2" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                </sequence>
            </complexType>
        </element>
        <element name="stuff">
            <complexType>
                <sequence>
                    <element name="attr3" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                    <element name="attr4" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                </sequence>
            </complexType>
        </element>
    </sequence>
</complexType>

(我只是在学习如何使用.xsd's,所以用文字说出来:只有attr1attr2可以进入bar,只有attr3 }和attr4可以进入stuff

基本上,在实际情况中,有太多标签可以手动将它们分开。我想知道是否有办法检查模式是否适合于需要分类的元素。如果他们属于一个类别,他们应该去那个类别。

感谢所有帮助,谢谢!

修改

@ Alejandro的代码适用于上面的基本伪代码,但我在我的文件中实现它时遇到了麻烦,这些文件更复杂。出于这个原因,我添加了一个更复杂的例子:

传入数据

<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
    <sch:nesting>
        <sch:myGroup>
            <sch:mustHaveData>asdf</sch:mustHaveData>

            <sch:attr1>val1</sch:attr1>
            <sch:attr2>val2</sch:attr2>
            <sch:attr3>val3</sch:attr3>
            <sch:attr4>val4</sch:attr4>
        </sch:myGroup>
        <sch:myGroup>
            <sch:mustHaveData>asdf2</sch:mustHaveData>

            <sch:attr1>val5</sch:attr1>
            <sch:attr2>val6</sch:attr2>
            <sch:attr3>val7</sch:attr3>
            <sch:attr4>val8</sch:attr4>
        </sch:myGroup>
    </sch:nesting>
</sch:foo>

所需的传出数据:

<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val1</sch:attr1>
            <sch:attr2>val2</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val3</sch:attr3>
            <sch:attr4>val4</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val5</sch:attr1>
            <sch:attr2>val6</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val7</sch:attr3>
            <sch:attr4>val8</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
</sch:fooOut>

模式文件中的某处:(并且比上次准确一些)

<complexType name="anotherGroup">
    <sequence>
        <element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
        <element name="bar" type="barListType" minOccurs="0" maxOccurs="1" />
        <element name="stuff" type="stuffListType" minOccurs="0" maxOccurs="1" />
    </sequence>
</complexType>

<!-- in another .xsd -->
<complexType name="barListType">
    <group ref="barGroup" maxOccurs="unbounded" />
</complexType>

<complexType name="stuffListType">
    <group ref="stuffGroup" maxOccurs="unbounded" />
</complexType>

<!-- in yet another .xsd -->
<group name="barGroup">
    <choice>
        <element name="attr1" type="blah1" minOccurs="0" maxOccurs="1" />
        <element name="attr2" type="blah2" minOccurs="0" maxOccurs="1" />
        <!-- etc -->
    </choice>
</group>

<group name ="stuffGroup">
    <choice>
        <element name="attr3" type="blah3" minOccurs="0" maxOccurs="1" />
        <element name="attr4" type="blah4" minOccurs="0" maxOccurs="1" />
        <!-- etc -->
    </choice>
</group>

最后,我的xsl文件     

    <xsl:output method="xml" encoding="UTF-8" />

    <xsl:param name="schema-name" select="'myXsd.xsd'" />
    <xsl:template match="/">
        <xsl:apply-templates select="document($schema-name)/xs:complexType[@*]" />
        <xsl:apply-templates select="sch:nesting"/>
    </xsl:template>

    <xsl:template match="sch:nesting/xs:element[xs:complexType]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <xsl:template match="sch:nesting/xs:element[not(xs:complexType)]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:value-of select="/*/sch:*[name()=current()/@name or
                                  substring-after(name(),':')=current()/@name]"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="sch:nesting">
        <xsl:element name="anotherGroup">
            <xsl:element name="name">
                <!-- Whatever -->
            </xsl:element>

            <xsl:apply-templates /> <!-- I want to drop the data here, but this is definitely wrong -->

        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

再次感谢您的帮助!

编辑2

所以我忘记了一个关于我的数据文件的小改动。除了布局之外,其他所有内容都应该是相同的,它是这样嵌套的:

<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
    <sch:nesting>
        <sch:myGroup>
            <inner1>
                <sch:mustHaveData>asdf</sch:mustHaveData>

                <sch:attr1>val1</sch:attr1>
            </inner1>
            <inner2>
                <sch:attr2>val2</sch:attr2>
                <sch:attr3>val3</sch:attr3>
                <sch:attr4>val4</sch:attr4>
            </inner2>
        </sch:myGroup>
        <sch:myGroup>
            <inner1>
                <sch:mustHaveData>asdf2</sch:mustHaveData>

                <sch:attr1>val5</sch:attr1>
            </inner1>
            <inner2>
                <sch:attr2>val6</sch:attr2>
                <sch:attr3>val7</sch:attr3>
                <sch:attr4>val8</sch:attr4>
            </inner2>
        </sch:myGroup>
    </sch:nesting>
</sch:foo>

我在这里要说明的是,群组内是子类别,我需要匹配这些子类别中的所有内容。

亚历杭德罗,为了让这一切值得你花时间(因为你得到了如此惊人的帮助),你应该把你的回答放在一个新的答案中,当我尝试并让它起作用时,我会投票并接受答案。

再次感谢!你真是个救星!

编辑3

我想出了理想的结果。我更改了说

的行
<xsl:copy-of select="*[local-name()=document($schema-name)/*/*

<xsl:copy-of select="*/*[local-name()=document($schema-name)/*/*

这给了我额外的一点我需要的东西。

2 个答案:

答案 0 :(得分:3)

架构(“schema.xs”):

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <element name="fooOut">
        <complexType name="fooOut">
            <sequence>
                <element name="bar">
                    <complexType>
                        <sequence>
                            <element name="attr1" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                            <element name="attr2" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                        </sequence>
                    </complexType>
                </element>
                <element name="stuff">
                    <complexType>
                        <sequence>
                            <element name="attr3" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                            <element name="attr4" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                        </sequence>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
</schema>

输入:

<sch:foo xmlns:sch="http://www.whatever.com/schema">   
    <sch:attr1>val1</sch:attr1>   
    <sch:attr2>val2</sch:attr2>   
    <sch:attr3>val3</sch:attr3>   
    <sch:attr4>val4</sch:attr4>   
</sch:foo> 

样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:sch="http://www.whatever.com/schema">
    <xsl:param name="schema-name" select="'schema.xs'"/>
    <xsl:variable name="input" select="/"/>
    <xsl:template match="/">
        <xsl:apply-templates select="document($schema-name)/node()"/>
    </xsl:template>
    <xsl:template match="xs:element[xs:complexType]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="xs:element[not(xs:complexType)]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:value-of select="$input/*/sch:*[name()=current()/@name or
                                        substring-after(name(),':')=current()/@name]"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

结果:

<?xml version="1.0" encoding="UTF-16"?>
<fooOut xmlns="http://www.whatever.com/schema">
    <bar>
        <attr1>val1</attr1>
        <attr2>val2</attr2>
    </bar>
    <stuff>
        <attr3>val3</attr3>
        <attr4>val4</attr4>
    </stuff>
</fooOut>

注意:这是一个XSLT 1.0解决方案,但我认为使用XSLT 2.0可以做得更好.-另外,如果模式更合适(元素定义和类型定义)这个方法可以使用键。尽管更复杂,但另一种方法(输入驱动而非模式驱动)也可以完成,但我已经没时间了。

修改:现在,假设这些架构

schemaA.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <include schemaLocation="schemaB.xsd"/>
    <element name="fooOut" type="fooOut"/>
    <complexType name="fooOut">
        <element name="anotherGroup" type="anotherGroup" minOccurs="0" maxOccurs="unbounded"/>
    </complexType>
    <complexType name="anotherGroup">
        <sequence>
            <element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
            <element name="bar" type="barListType" minOccurs="0" maxOccurs="1" />
            <element name="stuff" type="stuffListType" minOccurs="0" maxOccurs="1" />
        </sequence>
    </complexType>
</schema>

schemaB.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <include schemaLocation="schemaC.xsd"/>
    <complexType name="barListType">
        <group ref="barGroup" maxOccurs="unbounded" />
    </complexType>
    <complexType name="stuffListType">
        <group ref="stuffGroup" maxOccurs="unbounded" />
    </complexType>
</schema>

schemaC.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <group name="barGroup">
        <choice>
            <element name="attr1" type="blah1" minOccurs="0" maxOccurs="1" />
            <element name="attr2" type="blah2" minOccurs="0" maxOccurs="1" />
            <!-- etc -->
        </choice>
    </group>
    <group name ="stuffGroup">
        <choice>
            <element name="attr3" type="blah3" minOccurs="0" maxOccurs="1" />
            <element name="attr4" type="blah4" minOccurs="0" maxOccurs="1" />
            <!-- etc -->
        </choice>
    </group>
</schema>

此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="xml" encoding="UTF-8" />
    <xsl:param name="schema-name" select="'schemaA.xsd'" />
    <xsl:variable name="input" select="/*/*/*" />
    <xsl:variable name="root" select="document($schema-name)/*/xs:element[not(..//xs:element/@ref = @name)]" />
    <xsl:variable name="uri" select="$root/../@targetNamespace" />
    <xsl:template match="/" name="root">
        <xsl:param name="schema" select="$root/../*"/>
        <xsl:choose>
            <xsl:when test="$schema[self::xs:include]">
                <xsl:call-template name="root">
                    <xsl:with-param name="schema" select="$schema[not(self::xs:include)]|document($schema[self::xs:include]/@schemaLocation)/*/*"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="$root">
                    <xsl:with-param name="schema" select="$schema"/>
                    <xsl:with-param name="input" select="$input"/>
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="xs:element">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:variable name="complex" select="xs:complexType|
                                             $schema[self::xs:complexType][@name=current()/@type]"/>
        <xsl:element name="{@name|@ref}" namespace="{$uri}">
            <xsl:choose>
                <xsl:when test="$complex">
                    <xsl:apply-templates select="$complex">
                        <xsl:with-param name="schema" select="$schema"/>
                        <xsl:with-param name="input" select="$input"/>
                    </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$input[local-name()=current()/@name and
                                  namespace-uri()=$uri]"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
    <xsl:template match="xs:group[@ref]">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:apply-templates select="$schema[self::xs:group][@name=current()/@ref]">
            <xsl:with-param name="schema" select="$schema"/>
            <xsl:with-param name="input" select="$input"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="xs:element[@name='name']" priority="1">
        <xsl:element name="{@name}" namespace="{$uri}">foobar</xsl:element>
    </xsl:template>
    <xsl:template match="xs:element[@maxOccurs='unbounded']">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:variable name="current" select="."/>
        <xsl:for-each select="$input">
            <xsl:element name="{$current/@name}" namespace="{$uri}">
                <xsl:apply-templates select="$schema[self::xs:complexType][@name=$current/@type]">
                    <xsl:with-param name="schema" select="$schema"/>
                    <xsl:with-param name="input" select="./*"/>
                </xsl:apply-templates>
            </xsl:element>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="*">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:apply-templates>
            <xsl:with-param name="schema" select="$schema"/>
            <xsl:with-param name="input" select="$input"/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

结果:

<fooOut xmlns="http://www.whatever.com/schema">
    <anotherGroup>
        <name>foobar</name>
        <bar>
            <attr1>val1</attr1>
            <attr2>val2</attr2>
        </bar>
        <stuff>
            <attr3>val3</attr3>
            <attr4>val4</attr4>
        </stuff>
    </anotherGroup>
    <anotherGroup>
        <name>foobar</name>
        <bar>
            <attr1>val5</attr1>
            <attr2>val6</attr2>
        </bar>
        <stuff>
            <attr3>val7</attr3>
            <attr4>val8</attr4>
        </stuff>
    </anotherGroup>
</fooOut>

注意:这样可行,但您的第二个问题(或问题)显示没有一般案例样式表。为什么?因为使用XSLT,您必须将输入(具有众所周知的模式)绑定到输出(也具有众所周知的模式)。所以这个特定的样式表可以完成这项工作:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sch="http://www.whatever.com/schema">
    <xsl:output method="xml" encoding="UTF-8" />
    <xsl:param name="schema-name" select="'schemaC.xsd'" />
    <xsl:template match="sch:foo">
        <sch:fooOut>
            <xsl:apply-templates/>
        </sch:fooOut>
    </xsl:template>
    <xsl:template match="sch:myGroup">
        <sch:anotherGroup>
            <sch:name>foobar</sch:name>
            <sch:bar>
                <xsl:copy-of select="*[local-name()=document($schema-name)/*/*[@name='barGroup']//@name]" />
            </sch:bar>
            <sch:stuff>
                <xsl:copy-of select="*[local-name()=document($schema-name)/*/*[@name='stuffGroup']//@name]" />
            </sch:stuff>
        </sch:anotherGroup>
    </xsl:template>
</xsl:stylesheet>

结果:

<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val1</sch:attr1>
            <sch:attr2>val2</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val3</sch:attr3>
            <sch:attr4>val4</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val5</sch:attr1>
            <sch:attr2>val6</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val7</sch:attr3>
            <sch:attr4>val8</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
</sch:fooOut>

答案 1 :(得分:0)

不确定你在这里寻找什么。 XSLT将从源文档复制到目标文档但是您告诉它。例如:

<sch:fooOut>
  <sch:bar>
    <xsl:copy>
      <sch1:attr1>
      <sch1:attr2>
    </xsl:copy>
  </sch:bar>
</sch:fooOut>

您在寻找什么样的验证?如果您正在寻找更具动态性的解决方案,最好使用XSLT以外的其他东西。