本文へ移動

こーげんやさい

Stormworks の .xml ファイルのパース上の罠

投稿:,最終更新:

この記事は Stormworks 第 2 Advent Calendar 2023 第 11 日目の記事です。

Google 検索からの流入数が増えているとお知らせをいただきましたが、キーワードを見る限りはこの記事を探してきた人ではないように思われました。そこで、記事タイトルをより明確なものに変更しました。プログラムでパースする際の罠の話です。(2024 年 4 月 7 日)

投稿が大変遅くなりました。11 日目の記事となります。Stormworks 関連の XML ファイルの罠の話です。

XML の属性に使える名前

XML の属性名の先頭にハイフン、ピリオド、数字を含めることはできません。

XML の勧告文書において、Name は次のように定義されています。

[Definition: A Name is an Nmtoken with a restricted set of initial characters.] Disallowed initial characters for Names include digits, diacritics, the full stop and the hyphen.

もっと明快な定義として、後に BNF 記法による定義があります。 開始タグに記述する属性名は、式 40 、式 41 より、Nameを用いることが分かります。

式 5 で定義されるNameは、式 4 で定義されるNameStartCharを 1 文字と 式 4a で定義されるNameCharを 0 回以上繰り返すものと 定義されています。

そして式 4a を見るとNameCharNameStartCharに ハイフン、ピリオド、数字、その他いくらかの文字を加えて定義されていることがわかります。

すなわち、属性名の先頭にはハイフン、ピリオド、数字、その他いくらかの文字を使えないということです。

Stormworks の XML の罠

さて、ここで Stormworks で扱う XML ファイルの適当な開始タグを見てみましょう。

<!-- Add-on の playlist.xml の例 -->
<spawn_transform 30="452.567108" 31="6.659514" 32="232.245178"/>...</spawn_transform>

おやおや、先に述べたルールに反して、属性名が30とか31とか32とかじゃないですか。

でも、こういった類の規格違反は(あまり容認したくないとはいえ)よくある話です。しかし、XML では厄介な問題が生じます。 なぜならば XML パーサはここで処理を中断して、少なくとも例外を返さねばならないためです。

Validating and non-validating processors alike MUST report violations of this specification’s well-formedness constraints in the content of the document entity and any other parsed entities that they read.

まともに作られたパーサはここでエラーを返して止まってしまいます。わたしは線路を引きたいだけなのに! でもどうすることもできないのです。

脱出口: ずるいパーサを使う

ずるいパーサを使うことでこの問題を回避できることがあります。大抵そういうのは高速さを売りにしたパーサです。

C++ であれば RapidXML、JavaScript では fast-xml-parser が 例外を返さないので、ずるいパースができます。どちらもちょっとずつ使ったことがありますが、作りが簡単で扱いやすいライブラリです。

いずれにしても…… こんな脱出口を使わねばならないのは、全部 Stormworks のせいです。(おわり)

参考文献