• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Serialize Your Deck with Positron [XML Serialization, XSD, C#]

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

Serialize Your Deck with Positron [XML Serialization, XSD, C#]

 

Written by Allen Lee

 

0. Table of Content

  • 1. Positron S
  • 2. xsd.exe
  • 3. From .xml to .xsd
  • 4. From .xsd to .cs
  • 5. Serialize Your Deck
  • 6. What's More...
  • R. References

 

1. Positron S

《Yu-Gi-Oh! Power of XLinq [C#, XLinq, XML]》中,我们体验了 XLinq 是如何简化我们的 XML 处理工作,但现阶段就把使用 XLinq 的程序部署到用户的电脑未免有点为时过早。这次,我们来看看采用业已成熟的 XML Serialization 技术的 Positron,为了标识使用不同技术的 Positron,我在其后加上一个标识字母,目前 Positron 有两个版本:

  • 1) Positron Q:Q 版 Positron 使用了 XLinq 技术,“Q”代表 Query
  • 2) Positron S:S 版 Positron 使用了 XML Serialization 技术,“S”代表 Serialization

注意:本文将沿用《Yu-Gi-Oh! Power of XLinq [C#, XLinq, XML]》中的 sample.xml 作为原始数据,而某些设计决策也将基于该文的部分分析,如果你没有读过该文,我强烈建议你先浏览一遍。

 

2. xsd.exe

xsd.exe 是一个神奇的转换工具,它提供了

  • 1) XDR to XSD
  • 2) XML to XSD
  • 3) XSD to DataSet
  • 4) XSD to Classes
  • 5) Classes to XSD

等一系列的转换功能。当你用它来生成代码文件时,如果你没有明确指示使用何种语言,它将默认生成 .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>

这里需要注意的有两点:

  • 1) 枚举类型必须为命名类型,否则 xsd.exe 会忽略之并把 category 映射为 String 类型
  • 2) 枚举类型的基类型必须为 xs:string 或兼容类型,否则 xsd.exe 不会将之当作一回事

然后,把 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">
        

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C语言实例-计算自然数的和发布时间:2022-07-13
下一篇:
[C#]async和await刨根问底发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap