使用XSLT根据XSD转换XML

我想创建一个可以转换XML的XSLT,以便在输出XML(来自XSLT)中排除未在XSD中定义的所有元素和属性。

可以说你有这个XSD。

<xs:element name="parent">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="keptElement1" />
            <xs:element name="keptElement2" />
        </xs:sequence>

        <xs:attribute name="keptAttribute1" />
        <xs:attribute name="keptAttribute2" />
    </complexType>
</xsd:element>

你有这个输入XML

<parent keptAttribute1="kept" 
    keptAttribute2="kept" 
    notKeptAttribute3="not kept" 
    notKeptAttribute4="not kept">

    <notKeptElement0>not kept</notKeptElement0>
    <keptElement1>kept</keptElement1>
    <keptElement2>kept</keptElement2>
    <notKeptElement3>not kept</notKeptElement3>
</parent>

然后我想让输出Xml看起来像这样。

<parent keptAttribute1="kept" 
    keptAttribute2="kept">

    <keptElement1>kept</keptElement1>
    <keptElement2>kept</keptElement2>
</parent>

我可以通过指定元素来做到这一点,但就我的xslt技能而言,这只是一个问题。 对于所有元素和所有属性,我通常都会遇到问题。


这里面临两个挑战:(1)识别模式中声明的元素名称和属性集合,并为本地声明提供适当的上下文信息;(2)编写XSLT以保留匹配这些名称或名称的元素和属性,上下文。

还有第三个问题,即明确指出“在XSD架构中定义(或未定义)的元素和属性”。 为了讨论的目的,我假定你的意思是元素和属性可以被绑定到模式中的元素或属性声明,验证集(a)植根于输入文档树中的任意点,(b)以顶级元素声明或属性声明。 这个假设意味着几件事情。 (a)本地元素声明只会匹配上下文中的内容 - 在您的示例中,只有当它们是parent孩子时, keptElement1keptElement2才会保留,否则不会。 (b)不能保证输入中的元素实际上会被绑定到所涉及的元素声明:如果他们的祖先之一是本地无效的,那么事情在XSD 1.0和1.1中都会变得非常复杂。 (c)我们不允许从命名类型定义开始验证; 我们可以,但听起来好像这就是你感兴趣的东西。(d)我们不允许从本地元素或属性声明开始验证。

有了这些明确的假设,我们可以转向你的问题。

第一项任务要求您制作(a)所有元素和属性以及架构中顶级声明的列表,以及(b)从它们可到达的所有元素和属性。 对于顶层声明,我们需要记录的是对象的类型(元素或属性)和扩展名称。 对于本地对象,我们需要类型的对象和顶层元素声明的完整路径。 对于您的示例架构,列表(a)包含

  • 元素父母
  • (我正在使用大写字母命名空间名称来扩展名称的约定;有人称这个符号为克拉克符号。)

    列表(b)包含

  • 元素{}父/ {}保持元素1
  • 元素{}父/ {}保持元素2
  • 属性{} parent / {} keptAttribute1
  • 属性{}父/ {}保持属性2
  • 在更复杂的模式中,当您执行生成此列表的过程时,会有一定的簿记。

    您的第二项任务是编写一个XSLT样式表,该元素和属性保留在列表中并放弃其余部分。 (我在这里假设,当你删除一个元素时,你也删除了它的所有内容;你的问题是关于元素的,而不是标签。)

    对于列表中的每个元素,使用列表中给出的上下文编写适当的标识转换:

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

    您可以为每个元素编写单独的模板,也可以在匹配模式中编写多个元素:

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

    对于列表中的每个属性,请执行相同的操作:

    <xsl:template match="parent/@keptAttribute1">
      <xsl:copy/>
    </xsl:template>
    

    覆盖元素和属性的默认模板,以禁止所有其他元素和属性:

    <xsl:template match="*|@*"/>
    

    [或者,如DrMacro所建议的那样,您可以在XSLT中编写函数或命名模板来查阅您在任务1中生成的列表,而不是将其写入具有明确匹配模式的重复模板中。 根据你的背景,你可能会发现这种方法可以让你更容易或更难理解样式表在做什么。]


    这不能用通用XSLT处理来完成,因为XSLT引擎不知道XSD。

    这留下了几个选择:

  • 使用XSLT直接处理XSD文档以确定哪些元素类型是实际声明的,然后在变换中使用该信息。 例如,如果一个元素位于不受XSD模式控制的名称空间中,那么您知道它没有被定义,或者如果该元素的名称空间由具有“不严格”验证的xs:any元素指定,那么您知道它是未宣布。

  • 使用Saxon的商业版本,该版本提供XSD解析和验证,并提供对由XSD处理添加到元素的附加属性的访问。 有关详细信息,请参阅Saxon文档。

  • Apache xerces项目包括Java中的XSD解析器,可用于处理复杂的XSD以执行您所需的任何操作,例如构建由给定模式管理或不管理的元素类型或名称空间的列表。 因此,如果您的模式相对静态,则预处理模式以构建简单的数据文件可能是最有效的,您的XSLT可以在处理文档时使用该数据文件。

    你没有说你是否可以使用XSLT 2,但是如果可以的话,一般的解决方案是定义一个函数来确定是否声明了给定的元素或属性,然后将该函数用作标准标识变换的一部分。 使用XSLT 1,您可以使用命名模板获得相同的效果。

    例如:

    <xsl:function name="local:isGoverned" as="xs:boolean">
       <xsl:param name="context" as="node()"/>
       <xsl:variable name="isGoverned" as="xs:boolean">
       <!-- Do whatever you do to determine governedness,
            whether this is to look at your collected data
            or use Saxon-provide info or whatever.
        -->
      </xsl:variable>
      <xsl:sequence select="$isGoverned"/>
    </xsl:function>
    

    然后在你的身份转换中:

    <xsl:template match="*">
      <xsl:copy>
        <xsl:apply-templates 
          select="
             @*[local:isGoverned(.)], 
             (*[local:isGoverned(.)] | 
              node())"
        />
      </xsl:copy>
    </xsl:copy>
    
    <xsl:template match="@* | text() | comment() | processing-instruction()">
      <xsl:sequence select="."/>
    </xsl:template>
    

    这将只会传递由XSD管理的那些元素和属性,但是您会发现这一点。

    艾略特

    链接地址: http://www.djcxy.com/p/34333.html

    上一篇: Transforming a XML according to a XSD using XSLT

    下一篇: XPATH Select child elements whose type's element name is xs:simpleType