枚举大小写_不区分大小写的枚举

枚举大小写

在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>元素,您将遵循的算法是:

  1. 写左括号。
  2. 写下每个字母的大小写值。
  3. 写一个右括号。
  4. 如果这不是最后一个<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"/>

对于任何给定的字符串,正确的值是以下内容的串联:

  1. 以( A|a )格式写入的当前字母的值。
  2. 以( 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!

在我们的样本文档中,值fmarriedorange都区分大小写; 输入FMarriedOrAnGE会导致错误。 如果将这些非法值放入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

枚举大小写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值