我正在使用 XML 模式生成器为某个系统生成的一组 XML 文档创建模式。我使用这个生成的模式来生成 Java 代码来读取和写入 XML。我对生成的架构进行了某些手动更改,以便生成的 Java 代码对我更有用。例如,我添加枚举。
系统会不时更新,其生成的 XML 格式也会发生变化。这时,我需要重新生成架构。由于我对之前生成的模式进行了手动更改,因此我需要将现有的手动调整模式与新生成的模式进行比较,并手动移动相关更改,以避免破坏我的手动更改。我使用 Beyond Compare 来做到这一点。
我遇到的困难是,生成的模式的结构很大程度上取决于我用来生成模式的 XML 文档的某些特征——这些特征对我的用例来说并不重要。
每个生成的复杂类型都有一个序列作为其直接子代。没事儿。有时,除了元素子级之外,这些序列还具有选择或序列子级。通过像这样放置这些构造,生成器试图确保如果某些元素从未一起出现在源文档中,则它们将不会被允许一起出现在经过验证的文档中,并且如果某些元素总是一起出现在源文档,它们必须始终一起出现在生成的文档中。
我不关心这些事情。我宁愿有一个更简单、更宽松的模式,其中序列没有选择或序列子级,除非它们确实有必要。
我尝试编写 XSLT 来执行此操作,但它不起作用。
<xsl:template match="choice/sequence">
<xsl:for-each select="*">
<xsl:copy>
<xsl:variable name="att" as="attribute() *">
<xsl:attribute name="minOccurs">0</xsl:attribute>
</xsl:variable>
<xsl:apply-templates select="(@*[not(local-name() eq 'minOccurs')], $att)">
<xsl:sort select="local-name()"/>
</xsl:apply-templates>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
<xsl:template match="sequence/choice[not(@*)]">
<xsl:for-each select="*">
<xsl:copy>
<xsl:variable name="att" as="attribute() *">
<xsl:attribute name="minOccurs">0</xsl:attribute>
</xsl:variable>
<xsl:apply-templates select="(@*[not(local-name() eq 'minOccurs')], $att)">
<xsl:sort select="local-name()"/>
</xsl:apply-templates>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
<xsl:template match="sequence/sequence[@minOccurs eq '0']">
<xsl:for-each select="*">
<xsl:copy>
<xsl:variable name="att" as="attribute() *">
<xsl:attribute name="minOccurs">0</xsl:attribute>
</xsl:variable>
<xsl:apply-templates select="(@*[not(local-name() eq 'minOccurs')], $att)">
<xsl:sort select="local-name()"/>
</xsl:apply-templates>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
这试图做的是:在我有选择/序列的地方,删除序列,使其子代成为选择的子代(将它们从孙子提升为子代)。为了使生成的模式验证所有与原始文档相同的文档,还必须确保所有提升的元素上的 @minOccurs=0。
它将相同的逻辑应用于序列/选择,其中选择没有属性。这是为了排除具有 @maxOccurs=unbounded 的选择,因为这种转换是非法的。
它也将其应用于序列/序列,其中子序列具有@minOccurs=0,同样的原因。
这是可行的,除了在我生成的模式中的一种情况下,我有一个像这样的构造:
<xs:element name="blah1">
<xs:complexType>
<xs:sequence>
<xs:element ref="blah2"/>
<xs:choice>
<xs:element ref="blah3"/>
<xs:sequence>
<xs:element ref="blah4"/>
<xs:element ref="blah5"/>
</xs:sequence>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
我从中得到的是:
<xs:element name="blah1">
<xs:complexType>
<xs:sequence>
<xs:element ref="blah2"/>
<xs:element minOccurs="0" ref="blah3"/>
<xs:sequence minOccurs="0">
<xs:element ref="blah4"/>
<xs:element ref="blah5"/>
</xs:sequence>
</xs:sequence>
</xs:complexType>
</xs:element>
它正确地消除了选择。但它并没有消除内部序列,而我绝对希望它这样做。
我不是 XSLT 专家,但我认为通过在我自己的模板中使用 apply-templates ,它会递归地应用模板,并且它应该处理这种情况。
尝试直接在子节点上使用
xsl:apply-templates
,而不是像之类的东西
<xsl:template match="sequence/choice[not(@*)]">
<xsl:for-each select="*">
<xsl:copy>
使用
<xsl:template match="sequence/choice[not(@*)]">
<xsl:apply-templates/>
</xsl:template>
您可能需要使用一种模式来确保可以填写 minOccurs 属性,或者您可以编写一个与添加属性的默认模式中的正确谓词匹配
sequence/choice
的模板。
目前,通过使用
for-each
,您正在破坏部分递归意图。