LoginSignup
0
0

More than 3 years have passed since last update.

Unity で xsd(xml Schema)ファイルを用いて xml ファイルを検証する

Last updated at Posted at 2020-05-28

本記事は Unity 2019.3.3f1 及び Microsoft Visual Studio Community 2019 version 16.5.4 を使用しております。

本記事の内容

タイトルの通り、xsd ファイルを用いて xmlファイルの検証をします。
第1項 では、外部 xml,xsd ファイルから読み込み、検証を行い、
第2項 では、Resource.Load を用いて検証を行います。

本記事で使う xsd,xml ファイルは以下になります。

Event.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="hogehoge/Event"
          elementFormDefault="qualified"
          xmlns="http://tempuri.org/XMLSchema.xsd"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:Event="hogehoge/Event"
          xmlns:Status="hogehoge/Status">
<xs:import schemaLocation="Status.xsd" namespace="hogehoge/Status"/>
  <xs:element name="Event">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Message">
          <xs:complexType mixed="true">
            <xs:attribute name="Name" type="Status:Name"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>
Status.xsd

<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="hogehoge/Status"
          elementFormDefault="qualified"
          xmlns="http://tempuri.org/XMLSchema.xsd"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:Status="hogehoge/Status">

  <xs:simpleType name="Name">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Anyone"/>
      <xs:enumeration value="Someone" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

Message.xml
<?xml version="1.0" encoding="utf-8"?>
<Event xmlns="hogehoge/Event">
  <Message Name="Anyone">
    私はどこかにいるだれか
  </Message>
  <Message Name="Someone">
    僕はここにいるだれか
  </Message>
</Event>

1.外部 xsd,xml ファイルを読み込み、検証する

まず、外部 xsd,xml ファイルを読み込む場合には以下のようになります。

XmlGetter.cs
/// 変数の先頭の p は passed(引数)
/// l は local(メソッドスコープ内)
/// m は member(プロパティ)の略語です。

public static class XmlGetter
{
    public static XmlSchemaSet AddSchemaFromXsd(string pSchemaPath)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        XmlReader lSchemaReader = null;

        string targetNamespace = "hogehoge/Event";
        try
        {
            lSchemas.Add(targetNamespace, lSchemaReader = XmlReader.Create(pSchemaPath));

        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);
        }
        finally
        {
            if (lSchemaReader.ReadState != ReadState.Closed)
            {
                lSchemaReader.Close();
            }
        }
        return schemas;
    }

    public static XDocument GetXDocumentFromXml(string pXmlPath, XmlSchemaSet pSchemas)
    {

        XmlReaderSettings lSettings = new XmlReaderSettings();

        lSettings.ValidationType = ValidationType.Schema;

        lSettings.Schemas = pSchemas;

        XmlReader lXmlReader = null;
        XDocument pEventXml = null;
        try
        {
            lXmlReader = XmlReader.Create(pXmlPath, lSettings);
            pEventXml = XDocument.Load(lXmlReader);
        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);

        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
        }
        finally
        {
            if (lXmlReader.ReadState != ReadState.Closed)
            {
                lXmlReader.Close();
            }
        }
        return pEventXml;
    }
}
XmlGetTest.cs
public class XmlGetTest:MonoBehavior
{
    public void Awake()
    {
        string lSchemaPath = "Event.xsd";
        XmlSchemaSet lSchemas = AddSchemaFromXsd(lSchemaPath);
        string lXmlPath = "Event.xml";
        XDocument lXDocument = GetXDocumentFromXml(lXmlPath, lSchemas);
    }

}

これは、Unity のプロジェクトの外に XML,XSD ファイルが共にある場合に使えます。
その代わりに、プロジェクトをビルドした後に、自分でフォルダをビルドフォルダに加えなければいけません。
path は自分のファイルへのパスに適時置き換えて下さい。

xmlReaderSetting の ValidationType に Schema をセットすることで、Load 時に検証も行っています。

2.Resource.Load で xsd ファイルを読み込む

しかし、Unity で Resource フォルダ下に XML,XSD ファイルを置きたい場合があります。今回はその例を紹介します。

Resouce.Load を使う場合、そのファイル形式は Unity で対応していなければいけません。

2-1.ScriptedImporter を使う

Unity が対応していないファイルを使う場合、ScriptedImporter クラスを使います。

ScriptedImporter の詳しい説明はテラシュールブログさんを
参照して下さい。
テラシュールブログさん:
http://tsubakit1.hateblo.jp/entry/2017/12/14/012746

ScriptedImporter を使って xsd を TextAsset として扱うクラスを作ります。

XsdImporter.cs
[ScriptedImporter(1, "xsd")]
public class XSDImporter : ScriptedImporter
{
    public override void OnImportAsset(AssetImportContext ctx)
    {
        TextAsset lXsd;

        try
        {
            lXsd = new TextAsset( File.ReadAllText(ctx.assetPath) );
            ctx.AddObjectToAsset("XSD", lXsd);
            ctx.SetMainObject(lXsd);

        }
        catch (Exception e)
        {
            Debug.LogError(e.Message);
        }
    }
}

これで xsd ファイルを Resource.Load で読み込むことが出来るようになりました。

2-2.TextAsset を StringReader を経由して XmlReader インスタンスを生成する。

xsd ファイルを TextAsset として使う場合、StringReader クラスを使い、XmlReader インスタンスを生成します。

XmlGetter.cs

    public static XmlSchemaSet AddSchemaFromTextAsset(TextAsset pSchemaAsset)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        XmlReader lSchemaReader = null;
        StringReader lStringReader = null;
        string lTargetNamespace = "hogehoge/Event";
        try
        {
            lStringReader = new StringReader(pSchemaAsset.text);
            lSchemas.Add(lTargetNamespace, lSchemaReader =XmlReader.Create( lStringReader));

        }
        catch (XmlSchemaValidationException e)
        {
            Debug.LogError(e.Message);
            Debug.LogError("error line : " + e.LineNumber);
            Debug.LogError("error position : " + e.LinePosition);
        }
        finally
        {
            if (lSchemaReader.ReadState != ReadState.Closed)
            {
                lSchemaReader.Close();
            }
        }
        return lSchemas;

    }

AddSchemaFromTextAsset メソッドが、渡された TextAsset の text プロパティを使った StringReader を経由して XmlReader インスタンスを作成し、 XmlSchemaSet に変換します。

xml も同様に TextAsset から StringReader を用いて XmlReader インスタンスを作成して下さい。

2-3. import する xsd ファイルを XmlSchemaSet クラスに Add する

しかし、ここでエラーが発生します。

StringReader を使って XmlReader を作成すると、import した xsd ファイルを SchemaSet に Add する事が出来ません。

そこで、少々周りくどいですが、一つづつ SchemaSet を Add していく手段を取ります。

ついでにパスを一つづつ設定するのが嫌なのでパスジェネレーターも作りました。

PathGenerator.cs
public class PathGenerator
{
    string mBasePath;
    public PathGenerator(string pBaseath)
    {
        mBasePath = pBasepath;
    }

    public string GeneratePath(params string[] pPathes)
    {
        StringBuilder lGeneratePath =new StringBuilder().Append(mBasePath);
        foreach (string path in pPathes)
        {
            lGeneratePath.Append("/").Append(path);
        }
        return lGeneratePath.ToString();
    }
}
XmlGetter.cs
public enum eSchema
{
    Event,
    Status
}

public class XmlGetter
{

    /// 中略
    /// SchemaPath は自分のスキーマフォルダへのパスを入れて下さい。

    public Dictionary<string,string> GeneratePathPair()
    {
        string lEventStr = eSchema.Event.ToString();
        string lStatusStr = eSchema.Status.ToString();

        PathGenerator lTnsGenerator = new PathGenerator("hogehoge");
        PathGenerator lPathGenerator = new PathGenerator(SchemaPath);
        Dictionary<string, string> lTnsPathPairInXsd = new Dictionary<string, string>
        {
            [lTnsGenerator.GeneratePath(lEvent)] = lPathGenerator.GeneratePath(lEvent),
            [lTnsGenerator.GeneratePath(lStatus)] =
lPathGenerator.GeneratePath(lStatus)
        };
        return lTnsPathPairInXsd;
    }

    public static XmlSchemaSet GetSchemaFromXSD(Dictionary<string,string> pXsd)
    {
        XmlSchemaSet lSchemas = new XmlSchemaSet();
        StringReader lStringReader = null;
        XmlReader lSchemaReader = null;

        foreach (KeyValuePair<string, string> item in pXsd)
        {
            try
            {
                TextAsset xsdAsset = Resources.Load<TextAsset>(item.Value);
                lStringReader = new StringReader(xsdAsset.text);
                lSchemaReader = XmlReader.Create(lStringReader);
                lSchemas.Add(item.Key, lSchemaReader);

            }
            catch (XmlSchemaValidationException e)
            {
                Debug.LogError(e.Message);
                Debug.LogError("error line : " + e.LineNumber);
                Debug.LogError("error position : " + e.LinePosition);
            }
            finally
            {
                if (lStringReader != null)
                {
                    lStringReader.Close();
                }
                if (lSchemaReader.ReadState != ReadState.Closed)
                {
                    lSchemaReader.Close();

                }
            }
        }
        return lSchemas;
    }

}

何か指摘ありましたら宜しくお願いします。

参考にしたサイト。
xmlSchemaSet
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.schema.xmlschemaset?view=netcore-3.1

XmlReader
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.xmlreader?view=netcore-3.1

StringReader
https://docs.microsoft.com/ja-jp/dotnet/api/system.io.stringreader?view=netcore-3.1

XDocument
https://docs.microsoft.com/ja-jp/dotnet/api/system.xml.linq.xdocument?view=netcore-3.1

XmlSchema,Xml に関して参考にした書籍(「基礎 XML」著:山田祥寛)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0