枚举大小写
在developerWorks,我们一直在努力回答您的问题并满足您的需求。 最近,我收到了爱荷华州得梅因市的汤米琼斯的以下来信:
尊敬的瑞吉斯:
有什么办法可以在XML模式中进行不区分大小写的枚举? 如果元素的有效值为“ red”,“ blue”和“ green”,则我们希望用户使用大小写字母的任意组合作为这些值。 在XML Schema规范中找不到任何方法可以定义不区分大小写的枚举。 你能帮我们吗?
真诚的
爱荷华州得梅因市的汤米琼斯
好吧,汤米,我有好消息和坏消息。 坏消息是您无法使用XML Schema来完成您想做的事情。 好消息是我们有一个自动化解决方案,该解决方案符合标准,相当简单,不需要您做任何工作。
入门
首先,您不能直接做您想做的事。 解决此问题的方法是将枚举转换为正则表达式。 假设您的架构定义了以下数据类型:
<xsd:element name="favoriteColor">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="red"/>
<xsd:enumeration value="blue"/>
<xsd:enumeration value="green"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
要进行不区分大小写的比较,您需要将其转换为包含所有有效值的正则表达式。 例如,对于值"blue,"
,您将创建一个正则表达式,其中说:“这是大写或小写的B,然后是大写或小写的L,接着是大写或小写的U,然后是大写或小写的E。” 这意味着上面的枚举数据类型应如下所示:
<xsd:element name="favoriteColor">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:pattern value="((B|b)(L|l)(U|u)(E|e)) |
((G|g)(R|r)(E|e)(E|e)(N|n)) |
((R|r)(E|e)(D|d))"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
此正则表达式匹配"blue,"
"BlUE,"
"bLUe,"
以及拼写单词blue的任何其他大小写字母组合。 (您还可以通过生成一组定义所有大小写字母组合的<xsd:enumeration>
元素来解决此问题,但是它将比正则表达式大得多,尤其是当有效值是长字符串时。 )
更好的消息
因为XML Schema本身就是XML文档,所以您可以编写样式表,将枚举标记转换为您刚刚看过的正则表达式。 为此,您需要找到所有基于xsd:string
数据类型并包含<xsd:enumeration>
元素的<xsd:restriction>
<xsd:enumeration>
元素。 您想要的是一个样式表,该样式表将复制除您要查找的<xsd:restriction>
元素以外的所有现有模式。 然后,您将添加一个规则,该规则定义如何转换<xsd:enumeration>
元素。
这是一个样式表,定义了复制XML文档的基本规则。 这将是源文档中所有内容的默认规则。 稍后,您将添加用于转换<xsd:restriction>
元素的规则。
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:template match="*|@*|text()|comment()|processing-instruction()">
<xsl:copy>
<xsl:apply-templates select="*|@*|text()|comment()|
processing-instruction()" />
</xsl:copy>
</xsl:template>
<!-- Add the stuff that handles the enumerations here. -->
</xsl:stylesheet>
现在,您只需要编写转换<xsd:restriction>
元素的模板即可。 这是选择元素的XPath表达式:
<xsl:template match="xsd:restriction[@base='xsd:string']
[count(xsd:enumeration) > 0]">
如果您不熟悉XPath语法,这会告诉样式表处理器选择所有具有base='xsd:string'
属性并包含至少一个<xsd:enumeration>
元素的<xsd:restriction>
元素。 对于<xsd:restriction>
元素内的每个<xsd:enumeration>
元素,您将遵循的算法是:
- 写左括号。
- 写下每个字母的大小写值。
- 写一个右括号。
- 如果这不是最后一个
<xsd:enumeration>
,请添加竖线。
这是样式表中该部分的外观:
<xsl:template match="xsd:restriction[@base='xsd:string']
[count(xsd:enumeration) > 0]">
<xsd:restriction base="xsd:string">
<xsd:pattern>
<xsl:attribute name="value">
<xsl:for-each select="xsd:enumeration">
<!-- Step 1. Write a left parenthesis -->
<xsl:text>(</xsl:text>
<!-- Step 2. Write the upper- and lowercase letters -->
<!-- Step 3. Write a right parenthesis -->
<xsl:text>)</xsl:text>
<!-- Step 4. If this isn't the last enumeration, write -->
<!-- a vertical bar -->
<xsl:if test="not(position()=last())">
<xsl:text>|</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</xsd:pattern>
</xsd:restriction>
<xsl:template>
您可能已经注意到,此步骤跳过了写出每个字母的大写和小写值的困难步骤。 您将使用尾部递归和XSLT translate()
函数执行此操作。
尾递归是XSLT样式表中的常用技术。 您将使用命名模板来处理此问题; 命名的模板将自行调用,直到处理完字符串中的所有字母为止。 模板(在示例中命名为case-insensitive-pattern
)接收两个参数:要转换为正则表达式的字符串,以及字符串中应从其开始的位置。 这是命名模板的开始方式:
<xsl:template name="case-insensitive-pattern">
<xsl:param name="string"/>
<xsl:param name="index"/>
对于任何给定的字符串,正确的值是以下内容的串联:
- 以(
A|a
)格式写入的当前字母的值。 - 以(
A|a
)格式写入的剩余字母的值。 (如果没有字母,则该值为空;否则,您递归调用该模板。为此,您传递原始字符串并将起始位置增加一个。)
您将创建两个表示上述两个值的变量,然后使用<xsl:value-of>
元素输出它们的组合值。 对于当前字母,您输出左括号,字母的大写值,竖线,字母的小写值和右括号。 这是计算第一个变量的标记:
<xsl:variable name="current-letter">
<!-- Write a left parenthesis -->
<xsl:text>(</xsl:text>
<!-- Convert the current letter to uppercase -->
<xsl:value-of select="translate(substring($string, $index, 1),
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
<!-- Write a vertical bar -->
<xsl:text>|</xsl:text>
<!-- Convert the current letter to lowercase -->
<xsl:value-of select="translate(substring($string, $index, 1),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz')"/>
<!-- Write a right parenthesis -->
<xsl:text>)</xsl:text>
</xsl:variable>
关于XSLT translate()函数的一点认识
在继续之前,值得注意的是,XSLT translate()
函数需要三个字符串。 对于第一个字符串中的每个字符,第二个字符串( 'abcde...'
)中出现的任何字母都将被第三个字符串( 'ABCDE...'
)中的相应字母替换。 因此,如果第一个字符串是bed,
则函数调用translate('bed', 'abcde...', 'ABCDE...')
返回BED.
如果第一个字符串中的字符根本没有出现在第二个字符串中,则不会更改。 这意味着translate('bed7', 'abcde...', 'ABCDE...')
返回BED7.
如果需要,可以扩展函数调用中的字符串以包含西欧语言中使用的带重音符号。 (XSLT规范警告说, translate()
不足以用世界上所有语言进行大小写转换,因此请注意。)
现在,您计算所有剩余字母的值,每个字母将转换为( A|a
)格式。 如果当前字母的索引小于字符串的长度,则您将再次调用命名模板,传递原始字符串并将索引增加1。如果当前字母的索引等于字符串的长度,此变量是一个空字符串。
<xsl:variable name="remaining-letters">
<!-- If $index is less than the length of the string, -->
<!-- call the template again. -->
<xsl:if test="$index < string-length($string)">
<xsl:call-template name="case-insensitive-pattern">
<!-- The string parameter doesn't change -->
<xsl:with-param name="string" select="$string"/>
<!-- Increment the index of the current letter by 1 -->
<xsl:with-param name="index" select="$index + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:variable>
最后,使用<xsl:value-of>
元素和concat()
函数输出两个变量的<xsl:value-of>
。 这等效于其他编程语言中的return
语句。
<xsl:value-of select="concat($current-letter, $remaining-letters)"/>
因此,如果blue, red,
和green,
值有效,则可以使用样式表转换架构以生成新的架构。 使用该新架构,值BLUE, Blue, bLuE,
和blUE
均有效。
一个例子
这是一个说明样式表如何工作的示例。 您将使用一个模式来定义性别,婚姻状况和喜欢的颜色的枚举。 这是一个示例实例文档:
<?xml version="1.0"?>
<f:friend
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/developerWorks
friend.xsd"
xmlns:f="http://www.ibm.com/developerWorks">
<f:name>
<f:firstName>Jane</f:firstName>
<f:lastName>Doe</f:lastName>
</f:name>
<f:gender>f</f:gender>
<f:maritalStatus>married</f:maritalStatus>
<f:favoriteColor>orange</f:favoriteColor>
</f:friend>
作为此示例的一部分,您包括一小段Java代码XMLValidator.java
,该代码根据XML模式验证XML文档。 如果输入java XMLValidator friend.xml
,则会看到类似以下内容:
> java XMLValidator friend.xml
Your document contains no errors!
在我们的样本文档中,值f
, married
和orange
都区分大小写; 输入F
或Married
或OrAnGE
会导致错误。 如果将这些非法值放入friend.xml
,则会收到如下消息:
Error in friend.xml at line 10, column 25: cvc-type.3.1.3: The value 'F' of
element 'f:gender' is not valid.
Error in friend.xml at line 11, column 45: cvc-type.3.1.3: The value 'Married'
of element 'f:maritalStatus' is not valid.
Error in friend.xml at line 12, column 44: cvc-type.3.1.3: The value 'OrAnGE'
of element 'f:favoriteColor' is not valid.
您可以使用我们的XSLT样式表将原始模式转换为新的模式文档。
> java org.apache.xalan.xslt.Process -in friend.xsd -xsl convert-enumerations.xsl
-out insensitive-friend.xsd
接下来,更改XML文档的根元素以引用此新模式文件:
<f:friend
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/developerWorks
insensitive-friend.xsd"
xmlns:f="http://www.ibm.com/developerWorks">
如果现在对XML文档运行验证程序,您将再次收到消息,指出文档中没有错误。 文件case-insensitive.zip
包含您自己尝试所需的所有代码和示例。
好吧,汤米,我希望这能回答您的问题。 我们的解决方案相对简单,可以自动运行,并且基于XML标准。
有自己的问题吗? 请随时将它们发送给我们,我们将在我们的业余时间尝试答复他们。
翻译自: https://www.ibm.com/developerworks/xml/library/x-case/index.html
枚举大小写