首頁  >  文章  >  後端開發  >  在XML模式中擴充枚舉清單的範例程式碼詳解

在XML模式中擴充枚舉清單的範例程式碼詳解

黄舟
黄舟原創
2017-03-15 16:58:111471瀏覽

在清單中新增值是一種常見且必要的需求。模式設計者通常希望在系統架構中建立一種添加附加價值的方法,並且該附加價值在設計階段是未知的。模式設計者如何建立一個可擴展、易於實現的枚舉值清單?本文將介紹幾種實現這一目標的方法。 模式設計者和實現人員需要一種擴 在清單中新增值是一種常見且必要的需求。模式設計者通常希望在系統架構中建立一種添加附加價值的方法,並且該附加價值在設計階段是未知的。模式設計者如何建立一個可擴展、易於實現的枚舉值清單?本文將介紹幾種實現這一目標的方法。
模式設計者和實作人員需要一種擴充 XML 模式中現有枚舉清單的方法。不幸的是,XML 模式規範不允許在這些清單的建立過程中(參閱 參考資料)進行擴充。設計階段所選的值是固定的,而且都是可用的。儘管有這樣的限制,人們仍使用各種替代方案來實現清單擴展。很多使用現有的不能改變的模式的客戶經常提出這項要求。他們希望在添加新功能的同時保持向後相容性。本文中,您將會看到模式設計者如何克服障礙實現該功能。
枚舉清單 是特定資料點的一組指定值。例如,您也許透過固定的值清單查看國家代碼,包括 DE(德國)、US(美國)和 JP(日本)。根據給定的值集,當一個新國家被識別出時,如 TL(東帝汶)或 BA(波斯尼亞及黑塞哥維那),該怎麼辦?使用先前的名稱清單的客戶必須改變實作來容納新值。
當使用 XML 模式對資料建模時,枚舉值會明確列出。因此,國家代碼清單依序包含各個枚舉值。經常需要識別列表中的新值,而且必須將其容納到列表中,模式設計者試圖找到一種擴展列表的方法,實際上,是將這種方法構建到設計中,允許添加在設計時未知的附加價值。
建立可擴展的枚舉清單
在尋找此問題的解決方案時,受到四個關鍵標準的影響:
首先,要在設計階段之後擴展清單。不管是快速建立一個新的貿易夥伴還是建立時間關鍵型的新資料字段,在關鍵時刻進行擴展是一項實際需求。
其次,能夠在解析器中驗證值對於簡化實作是非常關鍵的。
第三,在單一週期內完成解析和驗證是至關重要的。這就避免了像 Genericode 解決方案一樣,在一個單獨的週期和解析器中進行驗證。對於某些設定來說,增加新技術需求會導致成本太高或太耗時。
最後,解決方案必須能夠向後相容原始的模式。不相容的清單變更不能稱為擴充。
有些人認為根本就不應該擴展枚舉列表。資料建模人員也許認為如果想讓模型包含更多資料、擴展模型,那麼可以根據產品建立模式 — 實際上,在需要時創建更大的模型並減少限制。如果能夠控制原始模式和資料模型,這樣做是可以的,這種方法也許是理想的方法。但是,如果您需要在設計階段之後進行實際擴展,這樣的方法是行不通的。
也有人認為擴充枚舉清單的關鍵是不使用 XML 模式驗證解析器。 Genericode(參閱 參考資料)建議在第二層對枚舉清單進行驗證,脫離初始的 XML 模式解析器驗證程序。這種理論是正確的,而且這種方法的應用會越來越廣泛。但是,如果要在一個解析週期內完成,這種解決方案是無法做到的。在某些情況下,不可能執行第二個驗證週期。
當然,您可以在新清單中建立新元素。但是,不能向後相容原始模式。我們的目標是在保持向後相容性的同時實現一個可擴展的清單(參閱 參考資料)。
對於本文的目標,這裡所做的假設是基於我與客戶打交道的經驗 —— 即用附加價值擴展現有枚舉清單的需求。另外,我假設在一個步驟內完成 XML 模式解析與驗證等操作。
擴充枚舉清單的必要條件
此擴充範例有四個必要條件:
允許在設計階段之後擴充枚舉清單。
用解析器驗證枚舉列表。
在一個週期內驗證枚舉清單。
維持和原始模式的向後相容性。
舉例來說,一個團隊需要處理一個區域產業協會的枚舉清單(或任意現有清單)為例,並根據使用修改模式組件。先前的模式提供 MaritalStatus 元件和值的枚舉列表,如 清單 1 所示。
清單 1. 婚姻狀況列舉清單

#
<xsd:simpleType name="MaritalStatusEnumType"> 
<xsd:restriction base="xsd:normalizedString"> 
<xsd:enumeration value="porced"/> 
<xsd:enumeration value="Married"/> 
<xsd:enumeration value="NeverMarried"/> 
<xsd:enumeration value="Separated"/> 
<xsd:enumeration value="SignificantOther"/> 
<xsd:enumeration value="Widowed"/> 
<xsd:enumeration value="Unknown"/> 
</xsd:restriction> 
</xsd:simpleType> 
<xsd:element name="MaritalStatus" type="MaritalStatusEnumType"/>

假設一個公司要使用這些值,另外,還要支持它的重要貿易夥伴使用另一個值。 CivilUnion 是擴充值,公司辨識出該值不屬於原始模式。但從語意上來說,使用現有元素 —MaritalStatus — 也是可以的。公司要如何實現?
回頁首
解決方案 1: 編輯原始模式使其包含新枚舉值
當然,編輯原始模式使其包含新枚舉值是最直接的方法。保留模式的本機副本,然後編輯這些模式以支援公司使用的枚舉值。
優點:易於實現
缺點:
需要編輯原始模式,這些模式將逐漸改變,以至於無法控制。如果擴展一個先前存在的列表,那麼創建者(貿易夥伴、協會等)可能要發布列表的新版本。您需要將編輯的內容傳播到每個新版本。
手動編輯模式會導致意外的編輯錯誤。
如果您無法(或不想)編輯原始模式,則需要一種替代方法。
回頁首
解決方案 2: 建立新枚舉列表並加入到原始列表中
第二個選擇是建立新枚舉列表,並將其加入到原始枚舉列表中。清單 1 顯示原始婚姻狀況清單。清單 2 顯示最新建立的枚舉清單。
清單 2. 新婚姻狀況列舉清單

<xsd:simpleType name="MyExtMaritalStatusEnumType"> 
<xsd:restriction base="xsd:normalizedString"> 
<xsd:enumeration value="CivilUnion"/> 
</xsd:restriction> 
</xsd:simpleType>

使用 a8a63e71ccce8bcd0a2f59429578398f 標記將其與原始清單結合,如 清單 3 所示。
清單3. 將兩個清單群組合起來

<xsd:simpleType name="MaritalStatusType_Union"> 
<xsd:union memberTypes="MyExtMaritalStatusEnumType MaritalStatusEnumType"/> 
</xsd:simpleType> 
<xsd:element name="MaritalStatus" type="MaritalStatusType_Union"/>

此解決方案仍需要對模式進行編輯— 即將元素MaritalStatus 由MaritalStatusType 類型轉換為MaritalStatusType_Union 類型。改動不大,但仍然有一些手動編輯工作。
優點:不改變原始枚舉清單。
缺點:
在設計階段所有的值必須是已知的,防止後期綁定解決方案。
需要 a8a63e71ccce8bcd0a2f59429578398f 標記支持,但有時該標記無法用工具實現。
回頁首
解決方案 3: 建立一個模式,並與原始枚舉類型結合
現在看一下有關眼睛顏色的人口資料用例。清單 4 顯示這一清單。
清單 4. Person Eye Color 列舉清單

<xsd:simpleType name="PersonEyeColorType"> 
<xsd:restriction base="xsd:string"> 
<xsd:enumeration value="Black"/> 
<xsd:enumeration value="Hazel"/> 
<xsd:enumeration value="Gray"/> 
<xsd:enumeration value="Brown"/> 
<xsd:enumeration value="Violet"/> 
<xsd:enumeration value="Green"/> 
<xsd:enumeration value="Blue"/> 
<xsd:enumeration value="Maroon"/> 
<xsd:enumeration value="Pink"/> 
<xsd:enumeration value="Dichromatic"/> 
<xsd:enumeration value="Unknown"/> 
</xsd:restriction> 
</xsd:simpleType>

接下來,建立採用新值的模式(一個正規表示式)。此模式是以 x: 為前綴的任意字串。 x: 是標準枚舉列表和擴展列表之間的描繪程序。清單 5 顯示此模式。
清單 5. 用於擴充的正規表示式

<xsd:simpleType name="StringPatternType"> 
<xsd:restriction base="xsd:string"> 
<xsd:pattern value="x:\S.*"/> 
</xsd:restriction> 
</xsd:simpleType>

最後,使用 a8a63e71ccce8bcd0a2f59429578398f 標記結合清單與模式,如 清單 6 所示。
清單 6. 列舉清單與擴充模式的結合

<xsd:simpleType name="MyExtPersonEyeColorType"> 
<xsd:union memberTypes="PersonEyeColorType StringPatternType"/> 
</xsd:simpleType> 
<xsd:element name="PersonEyeColor" type="MyExtPersonEyeColorType"/>

同一節點擁有標準與擴充值。兩個值很容易分離,而且都可以用解析器驗證,如 清單 7 所示。
清單 7. XML 實例範例

<PersonEyeColor>Black</PersonEyeColor> 
<PersonEyeColor>x:Teal</PersonEyeColor>

優點:
相同元素可用來所有資料。
用解析器對基本枚舉清單進行驗證。
清楚地分隔擴展值。
該解決方案允許在以後綁定新值。
缺點:
必須解析元素的內容,以決定是否已經被擴充。
模式解析器必須支援正規表示式。
需要 a8a63e71ccce8bcd0a2f59429578398f 標記支援。
回頁首
解決方案 4:使用單獨的欄位用於擴充
在該解決方案中,枚舉欄位不會變更。然而,您要在模式中設計一個擴充欄位來容納附加價值。在本例中,初始清單是依賴型的(就業受益者和受養人之間的關係),如 清單 8 所示。
清單 8. 依賴關係列舉清單

<xsd:simpleType name="DependentRelationshipEnumType"> 
<xsd:restriction base="xsd:string"> 
<xsd:enumeration value="AdoptedChild"/> 
<xsd:enumeration value="Brother"/> 
<xsd:enumeration value="Child"/> 
<xsd:enumeration value="ExSpouse"/> 
<xsd:enumeration value="Father"/> 
<xsd:enumeration value="Granddaughter"/> 
<xsd:enumeration value="Grandson"/> 
<xsd:enumeration value="Grandfather"/> 
<xsd:enumeration value="Grandmother"/> 
<xsd:enumeration value="LifePartner"/> 
<xsd:enumeration value="Mother"/> 
<xsd:enumeration value="Sister"/> 
<xsd:enumeration value="Spouse"/> 
<xsd:enumeration value="Extension"/> 
</xsd:restriction> 
</xsd:simpleType>

需要一個能容納新值的附加屬性 — extension—。清單 9 顯示了該屬性。
清單 9. 依賴關係的 extension 屬性

<xsd:complexType name="DependentRelationshipType"> 
<xsd:simpleContent> 
<xsd:extension base="DependentRelationshipEnumType"> 
<xsd:attribute name="extension" type="xsd:string"/> 
</xsd:extension> 
</xsd:simpleContent> 
</xsd:complexType> 
<xsd:element name="DependentRelationship" type="DependentRelationshipType"/>

清單 10 顯示一些反映 extension 的 XML 實例。
清單 10. 範例 XML 實例

<DependentRelationship>Child</DependentRelationship> 
<DependentRelationship extension="MyNewRelationship">Extension</DependentRelationship>

优点:
不需要编辑原始模式。
该解决方案允许在以后绑定新值。
在原始模式中显式设计 extension 方法。
缺点:
在设计阶段,必须在每个枚举列表中设计 extension 方法。
必须在元素中而不是在属性中设置枚举值。
回页首
解决方案 5: 基于文档的方法 —— 与字符串结合
注意:解决方案 5 和解决方案 6 违反了在一个周期内进行验证 这一要求。但是,我之所以在这里介绍它们,是因为在很多实际环境中可以使用这些方法。
在第 5 个解决方案中,使用 a8a63e71ccce8bcd0a2f59429578398f 标记将枚举列表与字符串结合。实际上,该解决方案提示接收系统哪些值是标准的(包括包装和拼写)。但实际上字符串字段可以存放任何值。因此,解析器并不验证值。相反,这些值在第二个周期或者在接收数据的应用程序中验证。有些 XML 组织就使用这样的方案。
清单 11 显示通过 a8a63e71ccce8bcd0a2f59429578398f 将一个枚举列表和 75eb557cfb45ea1d8dae02dc93b76dca 结合。因为任意值都可以是一个字符串,所以不用验证枚举列表。这些值建议使用标准值。
清单 11. 与字符串结合的 DayOfWeek 枚举列表

<xsd:simpleType name="DayOfWeekEnumType"> 
<xsd:restriction base="xsd:string"> 
<xsd:enumeration value="Sunday"/> 
<xsd:enumeration value="Monday"/> 
<xsd:enumeration value="Tuesday"/> 
<xsd:enumeration value="Wednesday"/> 
<xsd:enumeration value="Thursday"/> 
<xsd:enumeration value="Friday"/> 
<xsd:enumeration value="Saturday"/> 
</xsd:restriction> 
</xsd:simpleType> 
<xsd:element name="DayOfWeek" type="DayOfWeekEnumType"/> 
<xsd:simpleType name="ExtendedDayOfWeekType"> 
<xsd:union memberTypes="DayOfWeekEnumType xsd:string"/> 
</xsd:simpleType> 
<xsd:element name="DayOfWeek_solution5" type="ExtendedDayOfWeekType"/>

优点:可以添加任意的扩展值,即使在后期绑定时也可以添加。
缺点:
解析器不验证枚举值,在第二个步骤中才进行验证。
需要 a8a63e71ccce8bcd0a2f59429578398f 标记支持。
回页首
解决方案 6: 基于文档的方法 —— 使用 fca1af215fef1d5c1bced9a59797830f
要使用该方法,将实际的枚举值放到 21dba8562c1ffae18c64c17dc18f6cb0 标记内,同时将数据字段保留为一个简单字符串。清单 12 显示枚举值。
清单 12. 在 ae418a134aca541d60939a34685157c2 标记内的枚举值

<xsd:element name="DayOfWeek" type="xsd:string"> 
<xsd:annotation> 
<xsd:documentation> 
<!-- suggested enumerations --> 
<xsd:enumeration value="Sunday"/> 
<xsd:enumeration value="Monday"/> 
<xsd:enumeration value="Tuesday"/> 
<xsd:enumeration value="Wednesday"/> 
<xsd:enumeration value="Thursday"/> 
<xsd:enumeration value="Friday"/> 
<xsd:enumeration value="Saturday"/> 
</xsd:documentation> 
</xsd:annotation> 
</xsd:element>

优点:
可以添加任意的扩展值,即使在后期绑定时也可以添加。
只需要最简单的 XML 模式特性。
缺点:解析器不验证枚举值。
回页首
未讨论的方法
我省略了其他几种扩展枚举列表的解决方案。下面简单介绍了两种没有使用的方法:
使用 0f2bc009383268386b5007a40602d2a3 标记: 通常不使用 XML 模式的这一特性,而且一般无法用工具实现它。该方法经常被认为是避免重新定义的最佳实践。
使用 substitutionGroup 元素替换包含所有值的联合列表: 另外一种出色的解决方案,使用了替换组和联合。将原始列表与新列表联合以创建一个完整的枚举列表,然后使用 substitutionGroups 标记(或 44403ce233a2c37c854bc989d9695611 标记)替换一个全局作用域元素。该方法的缺点是替换不能派生有效的联合,替换需要两个组件来自相同的基类型。扩展和限制是替换的两个有效方法。但是,根据 XML 模式规范,联合并不是有效的派生技术(参阅 参考资料)。
回页首
结束语
XML 模式设计者和实现人员需要一种方法来扩展现有的枚举列表。因为一旦原始列表创建后,规范不允许进行扩展,因此需要找到一种方法实际实现扩展。实现人员可以使用本文的示例来设计和扩展枚举列表。每种方法都有优缺点,没有一种方法在所有用例中都是最佳方法。那么,应该使用哪种方法呢?
请考虑这些经验法则:
如果您习惯编辑原始枚举列表或模式,而且在设计阶段就知道所有要扩展的枚举值,最好使用 解决方案 1(手动编辑原始列表)或 解决方案 2(创建新列表并加入到原始列表中)。
如果想使用相同的语义元素来包含基本枚举列表和扩展枚举列表,可以考虑 解决方案 3(与模式联合)。
如果允许原始列表与扩展列表有不同的字段,可以使用 解决方案 4(独立的字段)。
如果不想在解析器中解析枚举值,可以考虑 Genericode 方法或使用 解决方案 5 或 解决方案 6。
这些指导原则可以使模式设计者找到实用的最佳实践,而且可以帮助他们创建易于实现、可扩展的枚举列表。
XML 模式和 XML 实例示例 ExtendEnumeratedListsCode.zip 2KB

以上是在XML模式中擴充枚舉清單的範例程式碼詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn