本記事は Unity 2019.3.3f1 及び Microsoft Visual Studio Community 2019 version 16.5.4 を使用しております。
本記事の内容
タイトルの通り、xsd ファイルを用いて xmlファイルの検証をします。
第1項 では、外部 xml,xsd ファイルから読み込み、検証を行い、
第2項 では、Resource.Load を用いて検証を行います。
本記事で使う xsd,xml ファイルは以下になります。
<?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>
<?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>
<?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 ファイルを読み込む場合には以下のようになります。
/// 変数の先頭の 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;
}
}
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 として扱うクラスを作ります。
[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 インスタンスを生成します。
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 していく手段を取ります。
ついでにパスを一つづつ設定するのが嫌なのでパスジェネレーターも作りました。
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();
}
}
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」著:山田祥寛)