しゅみぷろ

プログラミングとか

XML Schemaについて

Unityでゲーム内で実行されるイベントデータをXMLで管理しています。 このXMLの妥当性を検証するためのXML Schemaを書いてみたのでメモします。

まずXMLなのですが、大まかにこんな感じで使っています。

<?xml version="1.0" encoding="utf-8"?>

<Events>
  <Event eventID='ev_st00_001'>
    <!--システム制御-->
    <SystemControl>
      <TimeStop>true</TimeStop>
      <Input>
        <DisableGroup>Player</DisableGroup>
        <DisableGroup>SwitchMenu</DisableGroup>
      </Input>
    </SystemControl>
    <!--会話-->
    <Conversation name='Charactor1'>
      <Text>
        イベント1会話テスト1
      </Text>
      <Text>
        イベント1会話テスト2
      </Text>
    </Conversation>
    <!--カメラ移動-->
    <TargetTraceCameraControl>
      <OffsetX>5</OffsetX>
      <OffsetY>5</OffsetY>
      <OffsetZ>5</OffsetZ>
      <Target>Cube</Target>
    </TargetTraceCameraControl>
    <!--会話-->
    <Conversation name='Charactor2'>
      <Text>
        イベント1会話テスト3
      </Text>
      <Text>
        イベント1会話テスト4
      </Text>
    </Conversation>
    <!--カメラ移動-->
    <TargetTraceCameraControl>
      <OffsetX>0</OffsetX>
      <OffsetY>4.5</OffsetY>
      <OffsetZ>-20</OffsetZ>
      <Target>Player</Target>
    </TargetTraceCameraControl>
    <!--システム制御-->
    <SystemControl>
      <TimeStop>false</TimeStop>
      <Input>
        <EnableGroup>Player</EnableGroup>
        <EnableGroup>SwitchMenu</EnableGroup>
      </Input>
    </SystemControl>
  </Event>

  <Event eventID='ev_st00_002'>
    <Conversation>
      <Text>イベント2会話テスト1</Text>
    </Conversation>
  </Event>
</Events>

それぞれの要素がゲーム内で解釈されて該当するイベントを実行するのですが、 ここではそれぞれの要素の意味よりも、その構造を把握してください。

f:id:es_program:20160425235415p:plain

Events要素の中に1つのイベントを表すEventが複数入り、そのEvent要素にはSystemControl、Conversation、TargetTraceCameraControl等の要素が任意の順番で任意の個数入ります。以降は長くなるので説明を省きますが、図では以下のようになっています。

f:id:es_program:20160425235346p:plain

これに対するXML Schemaは以下のようになります。

<?xml version="1.0" encoding="utf-8"?>

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!--Events-->
  <xsd:element name="Events">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="Event" maxOccurs="unbounded"></xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>

  <!--Event-->
  <xsd:element name="Event">
    <xsd:complexType>
      <xsd:choice maxOccurs="unbounded">
        <xsd:group ref="EachEvent"></xsd:group>
      </xsd:choice>
      <xsd:attribute name="eventID" type="xsd:string"></xsd:attribute>
    </xsd:complexType>
  </xsd:element>

  <!--AttributeGroup-->
  <xsd:attributeGroup name="SelectEventObjectAttribute">
    <xsd:attribute name="name"></xsd:attribute>
    <xsd:attribute name="tag"></xsd:attribute>
    <xsd:attribute name="id"></xsd:attribute>
  </xsd:attributeGroup>

  <!--
    ↓******************************EachEventDefinition*********************************↓
  -->

  <!--EachEvent-->
  <xsd:group name="EachEvent">
    <xsd:choice>
      <xsd:element ref="SystemControl"></xsd:element>
      <xsd:element ref="Conversation"></xsd:element>
      <xsd:element ref="TargetTraceCameraControl"></xsd:element>
    </xsd:choice>
  </xsd:group>

  <!--SystemControl-->
  <xsd:element name="SystemControl">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:choice minOccurs="0">
          <xsd:element name="TimeStop" type="xsd:boolean"></xsd:element>
        </xsd:choice>
        <xsd:choice minOccurs="0">
          <xsd:element ref="Input"></xsd:element>
        </xsd:choice>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>

  <xsd:element name="Input">
    <xsd:complexType>
      <xsd:choice maxOccurs="unbounded">
        <xsd:element name="EnableGroup" type="xsd:string"></xsd:element>
        <xsd:element name="DisableGroup" type="xsd:string"></xsd:element>
      </xsd:choice>
    </xsd:complexType>
  </xsd:element>

  <!--Conversation-->
  <xsd:element name="Conversation">
    <xsd:complexType>
      <xsd:choice maxOccurs="unbounded">
        <xsd:element name="Text" type="xsd:string"></xsd:element>
      </xsd:choice>
      <xsd:attributeGroup ref="SelectEventObjectAttribute"></xsd:attributeGroup>
    </xsd:complexType>
  </xsd:element>

  <!--TargetTraceCameraControl-->
  <xsd:element name="TargetTraceCameraControl">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:choice minOccurs="0">
          <xsd:element name="OffsetX" type="xsd:float"></xsd:element>
        </xsd:choice>
        <xsd:choice minOccurs="0">
          <xsd:element name="OffsetY" type="xsd:float"></xsd:element>
        </xsd:choice>
        <xsd:choice minOccurs="0">
          <xsd:element name="OffsetZ" type="xsd:float"></xsd:element>
        </xsd:choice>
        <xsd:choice minOccurs="0">
          <xsd:element name="Target" type="xsd:string"></xsd:element>
        </xsd:choice>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

XML Schemaに関しては以下で詳しく取り扱ってくださっています。

また、C#で妥当性検証をする場合、以下のように記述します。

  private XElement GetElementFromXML()
  {
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
    settings.Schemas.Add(GetXmlSchema());
    using(var reader = XmlReader.Create("<XMLファイルパス>", settings))
    {
      return XElement.Load(reader);
    }
  }

  private XmlSchema GetXmlSchema()
  {
    XmlSchema schema = new XmlSchema();
    using(var reader = XmlReader.Create("<XSDファイルパス>"))
      schema = XmlSchema.Read(reader, null);
    return schema;
  }