使用XSLT根据XSD转换XML

Sti*_*ahl 3 xml xslt xsd

我想创建一个可以转换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>
Run Code Online (Sandbox Code Playgroud)

你有这个输入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>
Run Code Online (Sandbox Code Playgroud)

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

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

    <keptElement1>kept</keptElement1>
    <keptElement2>kept</keptElement2>
</parent>
Run Code Online (Sandbox Code Playgroud)

我可以通过指定元素来做到这一点,但这就是我的xslt技能所达到的目标.对于所有元素和所有属性,我一般都有问题.

小智 5

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

这留下了几个选项:

  1. 直接使用 XSLT 处理 XSD 文档以确定实际声明和未声明哪些元素类型,然后在转换中使用该信息。例如,如果一个元素位于不受 XSD 架构管理的命名空间中,那么您知道它没有定义,或者如果元素的命名空间是由 xs:any 具有“宽松”验证的元素指定的,那么您知道它是未声明。

  2. 使用 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>
Run Code Online (Sandbox Code Playgroud)

然后在你的身份转换中:

<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>
Run Code Online (Sandbox Code Playgroud)

这将产生仅通过由 XSD 管理的那些元素和属性的效果,但是您会发现这一点。

艾略特


C. *_*een 5

这里有两个挑战:(1)识别模式中声明的元素名称和属性集合,使用适当的本地声明上下文信息,以及(2)编写XSLT以保留与这些名称或名称匹配的元素和属性 - 和 - 上下文.

还有第三个问题,即明确指出"XSD架构中定义(或未定义)的元素和属性"的含义.出于讨论的目的,我假设您的意思是元素和属性,它们可以绑定到模式中的元素或属性声明,在验证集(a)中以输入文档树中的任意点为根,(b)从顶级元素声明或属性声明.这个假设意味着几件事.(a)局部元素声明仅匹配上下文中的内容 - 在您的示例中,keptElement1并且keptElement2仅在它们是子元素时才会保留parent,否则将保留.(b)无法保证输入中的元素实际上会与所讨论的元素声明绑定:如果其中一个祖先在本地无效,则在XSD 1.0和1.1中都会很快变得复杂.(c)我们不允许从命名类型定义开始验证; 我们可以,但听起来并不像你感兴趣的那样.(d)我们不允许从本地元素或属性声明开始验证.

有了这些假设,我们可以转向您的问题.

第一个任务要求您列出(a)模式中具有顶级声明的所有元素和属性,以及(b)可从它们访问的所有元素和属性.对于顶级声明,我们需要记录的是对象的类型(元素或属性)和扩展名称.对于本地对象,我们需要一种对象和顶级元素声明的完整路径.对于您的示例模式,列表(a)包含

  • element {} parent

(我使用在大括号中使用命名空间名称编写扩展名称的约定;有些人称之为Clark符号,对于James Clark.)

清单(b)包括

  • element {} parent/{} keepsElement1
  • element {} parent/{} keepsElement2
  • attribute {} parent/{} keepsAttribute1
  • attribute {} parent/{} keepsAttribute2

在更复杂的模式中,当您完成生成此列表的过程时,将会有一定量的簿记.

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

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

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

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

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

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

<xsl:template match="parent/@keptAttribute1">
  <xsl:copy/>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)

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

<xsl:template match="*|@*"/>
Run Code Online (Sandbox Code Playgroud)

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