教訓としての失敗談として読んでもらってもいいし、不平と読んでもらっても構わない。
XML に対する不平は色んなとこで書いてるけれど、まぁ個人的経験としては「懲りずにまたやっちゃった」的なことだったりもする、そんなハナシ。
今アタシがやってるある対象 XML は、ほんとは XML Schema がちゃんとあるヤツ。中見てみたらどうも非常に不十分なものということはわかってはいるけれど、そうであっても「クソまじめに XML Schema 読んでれば」、あるいはこの問題は起こらなかったのかも。なにせ「マジメに XML Schema を扱う」ためのインフラは標準 Python の範囲内では存在してないもんだから、どうしたって初期状態がサボりモードになり、「あー面倒だ、辞書にしちゃえばいいじゃん?」として「間違いが始まる」。
「組み込みのリストがない」ということについてはXML に対する不平では「リストであってくれぃ、とお祈りする必要がある」というような言い方で文句を言っているが、今回ワタシがやらかしちゃったミスは、これとはほんの少しだけ違う。すなわち、「順序が大事」(つまり上から順に読みやがれ」)であることが暗黙に期待されている XML をトップレベルから辞書にしてしまったもんだから、手にした辞書の扱いに苦慮するハメに。
つまり…
1 <?xml version="1.0" encoding="utf-8"?>
2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 <ItemGroup Label="ProjectConfigurations">
4 <ProjectConfiguration Include="DebugDLL|Win32">
5 <Configuration>DebugDLL</Configuration>
6 <Platform>Win32</Platform>
7 </ProjectConfiguration>
8 <ProjectConfiguration Include="DebugDLL|x64">
9 <Configuration>DebugDLL</Configuration>
10 <Platform>x64</Platform>
11 </ProjectConfiguration>
12 <!-- ... -->
13 </ItemGroup>
14 <PropertyGroup Label="Globals">
15 <Keyword>Win32Proj</Keyword>
16 <ProjectName>opus</ProjectName>
17 <ProjectGuid>{219EC965-228A-1824-174D-96449D05F88A}</ProjectGuid>
18 </PropertyGroup>
19 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
20 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
21 <ConfigurationType>StaticLibrary</ConfigurationType>
22 <PlatformToolset>v140</PlatformToolset>
23 </PropertyGroup>
24 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugDLL|Win32'" Label="Configuration">
25 <ConfigurationType>DynamicLibrary</ConfigurationType>
26 <PlatformToolset>v140</PlatformToolset>
27 </PropertyGroup>
28 <!-- ... -->
29 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
30 <ImportGroup Label="ExtensionSettings">
31 </ImportGroup>
32 <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
33 <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
34 <Import Project="common.props" />
35 </ImportGroup>
36 <!-- ... -->
37 <PropertyGroup Label="UserMacros" />
38 <ItemDefinitionGroup>
39 <ClCompile>
40 <!-- ... -->
41 <AdditionalOptions Condition="'$(Platform)'=='Win32'">/arch:IA32 %(AdditionalOptions)</AdditionalOptions>
42 </ClCompile>
43 <Lib>
44 <AdditionalOptions>/ignore:4221 %(AdditionalOptions)</AdditionalOptions>
45 </Lib>
46 <PreBuildEvent>
47 <Command>"$(ProjectDir)..\..\win32\genversion.bat" "$(ProjectDir)..\..\win32\version.h" PACKAGE_VERSION</Command>
48 <Message>Generating version.h</Message>
49 </PreBuildEvent>
50 </ItemDefinitionGroup>
51 <ItemGroup>
52 <!-- ... -->
53 </ItemGroup>
54 <ItemGroup>
55 <!-- ... -->
56 </ItemGroup>
57 <Choose>
58 <When Condition="'$(Configuration)'=='DebugDLL' or '$(Configuration)'=='ReleaseDLL'">
59 <ItemGroup>
60 <!-- ... -->
61 </ItemGroup>
62 </When>
63 <Otherwise>
64 <ItemGroup>
65 <!-- ... -->
66 </Otherwise>
67 </Choose>
68 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
69 <ImportGroup Label="ExtensionTargets">
70 </ImportGroup>
71 </Project>
このような XML をいきなりトップレベルからの辞書にすると、「ProjectConfigurations が決まってから Import を処理すのである」、のような処理順序に関する情報を丸ごと失ってしまう。
この XML、「Condition」なんてのが見えてるのからわかるように、また、変数(Microsoft 的には「マクロ」と呼んでいるみたい)の中身を知らないと処理を進められないものもあるため、処理しながら内部コンテキストを更新しながら読み進めていく必要があり、要するに「書かれた順序通りに処理」しないと、「まだ決まっていないコンテキストに基づき判定する」というおかしな状況が発生する。
「リストかもしれないしそうじゃないかもしれない」というのは要するにこういうことである。例にした XML は、「暗黙にリストであることが期待されている」ものだったのだ。Python データ構造ではつまりこういうことね:
1 project = [
2 {"ItemGroup": {...}},
3 {"PropertyGroup": {...}},
4 # ...
5 ]
組み込みのリストさえあればこんなことは起こらなかっただろう、という文句でもあるし、「この問題、何度も喰らってるんだから少しは学習せいや」という反省でもあったりする。なんだろな、定期的にやっちゃうんだよね、これ。
して、「あぅぁ…、トップレベルだけはリストにせんとダメだぁ、やり直しだやり直し…」に、目下激しくヘコみ中。いったん辞書から始めちゃってある程度作りこみが進んじゃってると、この改造は滅茶苦茶タイヘン。ほんと最初から書き直した方が早いんじゃないかしら、との闘い。参ったよ…。