2013-02-12 51 views
3

一个XSD一个XML我想创建一个XSLT,可以改变一个XML,使所有的元素和属性未在XSD定义被排除在输出XML(从XSLT) 。转化根据使用XSLT

可以说你有这个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技能达到的。对于所有元素和所有属性,我通常都会遇到问题。

回答

5

你这里有两个方面的挑战:(1)识别一组元素名称和模式中声明的属性,与局部声明适当的上下文信息,和(2)写XSLT保留元素和属性它们匹配那些名称或名称和上下文。

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

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

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

  • 元素{}父

(我使用括号写与命名空间名称扩展名的约定;有的称此为克拉克表示法,詹姆斯克拉克。)

列表(b)由

  • 元件的{}父/ {} keptElement1
  • 元素{}父/ {} keptElement2
  • 属性{}父/ {} keptAttribute1
  • 属性{}父/ {} keptAttribute2

在更复杂的模式,将有一定量的簿记当你经历产生这个列表的过程时。

您的第二项任务是编写一个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中,而不是用明确的匹配模式写出重复的模板。根据您的背景,您可能会发现这种方法可以更容易或更难以理解样式表在做什么。]

4

这不能与一般的XSLT处理这样做是因为XSLT引擎已经没有了XSD的知识。

这使得几个选项:

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

  2. 使用撒克逊的商业版本,它提供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支配这些元素和属性的效果,但是你弄清楚。

艾略特