在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Serialize Your Deck with Positron [XML Serialization, XSD, C#]
Written by Allen Lee
0. Table of Content
1. Positron S 在《Yu-Gi-Oh! Power of XLinq [C#, XLinq, XML]》中,我们体验了 XLinq 是如何简化我们的 XML 处理工作,但现阶段就把使用 XLinq 的程序部署到用户的电脑未免有点为时过早。这次,我们来看看采用业已成熟的 XML Serialization 技术的 Positron,为了标识使用不同技术的 Positron,我在其后加上一个标识字母,目前 Positron 有两个版本:
注意:本文将沿用《Yu-Gi-Oh! Power of XLinq [C#, XLinq, XML]》中的 sample.xml 作为原始数据,而某些设计决策也将基于该文的部分分析,如果你没有读过该文,我强烈建议你先浏览一遍。
2. xsd.exe xsd.exe 是一个神奇的转换工具,它提供了
等一系列的转换功能。当你用它来生成代码文件时,如果你没有明确指示使用何种语言,它将默认生成 .cs 文件。你可以使用 /l 参数来显示指定使用何种语言,xsd.exe 支持 CS、VB、JS 和 VJS 等语言。在这篇文章,我将会介绍 XML to XSD 和 XSD to Classes 这两种转换。
3. From .xml to .xsd 3.1 Generate sample.xsd with xsd.exe 打开 SDK Command Prompt,去到 sample.xml 所在的目录并输入 xsd sample.xml 然后按下 [Enter],xsd.exe 将在当前目录生成一个 sample.xsd 文件。但这个自动生成版的布局不便于我们对其展开讨论,于是我对其进行等效重排。方法是将原来的匿名类型变为命名类型并从其所属元素中分离出来,然后使用 <xs:element> 的 type 属性将这两者重新关联起来。重排后的版本如下:
sample.xsd
<?xml version="1.0" encoding="utf-8"?> <xs:schema id="cards" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <!-- MonsterCard --> <xs:complexType name="MonsterCard"> <xs:simpleContent msdata:ColumnName="monstercard_Text" msdata:Ordinal="8"> <xs:extension base="xs:string"> <xs:attribute name="img" type="xs:string" /> <xs:attribute name="category" type="xs:string" /> <xs:attribute name="name" type="xs:string" /> <xs:attribute name="attribute" type="xs:string" /> <xs:attribute name="level" type="xs:string" /> <xs:attribute name="type" type="xs:string" /> <xs:attribute name="atk" type="xs:string" /> <xs:attribute name="def" type="xs:string" /> </xs:extension> </xs:simpleContent> </xs:complexType> <!-- SpellCard --> <xs:complexType name="SpellCard"> <xs:simpleContent msdata:ColumnName="spellcard_Text" msdata:Ordinal="3"> <xs:extension base="xs:string"> <xs:attribute name="img" type="xs:string" /> <xs:attribute name="category" type="xs:string" /> <xs:attribute name="name" type="xs:string" /> </xs:extension> </xs:simpleContent> </xs:complexType> <!-- TrapCard --> <xs:complexType name="TrapCard"> <xs:simpleContent msdata:ColumnName="trapcard_Text" msdata:Ordinal="3"> <xs:extension base="xs:string"> <xs:attribute name="img" type="xs:string" /> <xs:attribute name="category" type="xs:string" /> <xs:attribute name="name" type="xs:string" /> </xs:extension> </xs:simpleContent> </xs:complexType> <!-- Cards --> <xs:complexType name="Cards"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="monstercards"> <xs:complexType> <xs:sequence> <xs:element name="monstercard" type="MonsterCard" nillable="true" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="spellcards"> <xs:complexType> <xs:sequence> <xs:element name="spellcard" type="SpellCard" nillable="true" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="trapcards"> <xs:complexType> <xs:sequence> <xs:element name="trapcard" type="TrapCard" nillable="true" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:element name="cards" type="Cards" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" /> </xs:schema> 3.2 <xs:choice> vs. <xs:all> 在 Cards 的类型定义中,xsd.exe 将其子元素的排布方式设置为 <xs:choice>,这个意味着其子元素 monstercards、spellcards 和 trapcards 只有一种出现,而该种子元素的出现次数可以任意。这明显不符合原设计理念。 all 指示所有的子元素可以以任意顺序出现,且每种子元素最多只能出现一次。我假设 Positron S 的用户懂得均衡卡组,即卡组中既有怪兽卡又有魔法卡和陷阱卡,并使不同种类的卡片数目达到一个恰当的比例。于是,我将三种子元素的排布方式改为: <xs:all>...</xs:all> 注意:如果我们没有显式为 <xs:all> 指明 minOccurs 和 maxOccurs 的值,它们都将会使用默认值1,即每种子元素都必须出现一次也只能出现一次。 3.3 xs:string vs. xs:int 在 MonsterCard 的类型定义中,xsd.exe 把 level、atk 和 def 三个属性的类型指定为 xs:string,但我们很清楚这些属性的值是整数,所以我把它们都改为 xs:int。这样做的好处不仅仅在于让人能从 sample.xsd 中了解到这三个属性的值是整数类型,更重要的是将来使用 xsd.exe 根据 sample.xsd 生成 sample.cs 时,MonsterCard 类中的 m_Level 字段以及 Level 属性能被自动映射为 Int32 类型。并且在反序列化时,让 XmlSerializer 为你进行数值的解析而不必亲自动手。 3.4 xs:string vs. xs:enumeration 我们知道 MonsterCard 的类型定义中的 category、attribute 和 type 其实是枚举类型,我希望将来使用 xsd.exe 生成代码文件时,它懂得把这些属性映射为 .NET 的枚举类型。为了达到这个目的,我们需要独立定制这些属性的类型,并使用 <xs:attribute> 的 type 属性进行类型关联,即我先前所说“等效重排”。下面我将以 MonsterCard 的 category 作为例子: 首先,我定义一个命名枚举类型:
<xs:simpleType name="MonsterCardCategory">
<xs:restriction base="xs:string"> <xs:enumeration value="Normal" /> <xs:enumeration value="Effect" /> <xs:enumeration value="Fusion" /> <xs:enumeration value="Ritual" /> </xs:restriction> </xs:simpleType> 这里需要注意的有两点:
然后,把 category 属性的 type 设置为 MonsterCardCategory:
<xs:attribute name="category" type="MonsterCardCategory" />
接着,我们可以用同样的方法处理其它枚举类型的属性。 3.5 <xs:simpleContent> vs. <xs:complexContent> 重读 sample.xsd,你会发现,无论是 MonsterCard、SpellCard 或者 TrapCard,都有着三个功能相同的成员:img、name 和 body text。为了减少重复,我决定对它们进行泛化,提取公共部分。 首先,我定义一个 Card 类型:
<xs:complexType name="Card" abstract="true">
<xs:simpleContent msdata:ColumnName="description" msdata:Ordinal="2"> <xs:extension base="xs:string"> <xs:attribute name="img" type="xs:string" use="required" /> <xs:attribute name="name" type="xs:string" use="required" /> </xs:extension> </xs:simpleContent> </xs:complexType> 注意:我将 Card 的 abstract 属性设为 true,这点很重要,它保证了在将来的 XML 文档中出现的是 Card 的继承类型而不是 Card 这个类型。这一点和程序语言的抽象类在设计理念上是一致的。 然后让 MonsterCard、SpellCard 和 TrapCard 继承 Card,要做到这点,我们可以修改 <xs:extension> 的 base 属性,使其指向 Card。 然而,<xs:extension> 用在 <xs:simpleContent> 或者 <xs:complexContent> 上会对 xsd.exe 所生成的代码产生不同的影响。对于前者,xsd.exe 会把 base 属性所指定的类型映射为类的一个字段,即我们通常说的 Composition;对于后者,情形就是我们所熟悉的 Inheritance。很明显,这里我们应该选用 Inheritance,因为 Card 的 abstract 属性被设为 true,如果使用 Composition 的话,抽象类 Card 作为类的一个字段而存在,必须有(非抽象)派生类才能产生实例变量,这样我们就重新回到 Inheritance 了。 现在,我用 SpellCard 来示范如何实现继承:
<xs:complexType name="SpellCard">
<xs:complexContent> <xs:extension base="Card"> <xs:attribute name="category" type="SpellCardCategory" /> </xs:extension> </xs:complexContent> </xs:complexType> 虽然这三种卡都有 category 属性,但因为该属性实际上具有不同的含义,并且类型也不同,所以不被纳入它们的共性。 3.6 cards.xsd 至此,我们已经完成了整个 XML Schema 的制作了:
cards.xsd
<?xml version="1.0" encoding="utf-8"?> <xs:schema id="cards" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <!-- enum MonsterCardCategory --> <xs:simpleType name="MonsterCardCategory"> <xs:restriction base="xs:string"> <xs:enumeration value="Normal" /> <xs:enumeration value="Effect" /> <xs:enumeration value="Fusion" /> <xs:enumeration value="Ritual" /> </xs:restriction> </xs:simpleType> <!-- enum MonsterAttribute --> <xs:simpleType name="MonsterAttribute"> <xs:restriction base="xs:string"> <xs:enumeration value="Earth" /> <xs:enumeration value="Water" /> <xs:enumeration value="Fire" /> <xs:enumeration value="Wind" /> <xs:enumeration value="Light" /> <xs:enumeration value="Dark" /> </xs:restriction> </xs:simpleType> <!-- enum MonsterType --> <xs:simpleType name="MonsterType"> <xs:restriction base="xs:string"> <xs:enumeration value="Dragon" /> <xs:enumeration value="Spellcaster" /> <xs:enumeration value="Zombie" /> <xs:enumeration value="Warrior" /> <xs:enumeration value="BeastWarrior" /> <xs:enumeration value="Beast" /> <xs:enumeration value="WingedBeast" /> <xs:enumeration value="Fiend" /> <xs:enumeration value="Fairy" /> <xs:enumeration value="Insert" /> <xs:enumeration value="Dinosaur" /> <xs:enumeration value="Reptile" /> <xs:enumeration value="Fish" /> <xs:enumeration value="SeaSerpent" /> <xs:enumeration value="Machine" /> <xs:enumeration value="Thunder" /> <xs:enumeration value="Aqua" /> <xs:enumeration value="Pyro" /> <xs:enumeration value="Rock" /> <xs:enumeration value="Plant" /> </xs:restriction> </xs:simpleType> <!-- enum SpellCardCategory --> <xs:simpleType name="SpellCardCategory"> <xs:restriction base="xs:string"> <xs:enumeration value="Normal" /> <xs:enumeration value="Continuous" /> <xs:enumeration value="Equip" /> <xs:enumeration value="Field" /> <xs:enumeration value="QuickPlay" /> <xs:enumeration value="Ritual" /> </xs:restriction> </xs:simpleType> <!-- enum TrapCardCategory --> <xs:simpleType name="TrapCardCategory"> <xs:restriction base="xs:string"> <xs:enumeration value="Normal" /> <xs:enumeration value="Counter" /> <xs:enumeration value="Continuous" /> </xs:restriction> </xs:simpleType> <!-- abstract class Card --> <xs:complexType name="Card" abstract="true"> |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论