バージョン・コントロール可能なXMLの作成

このトピックでは、チーム内で共有することを意図したXMLファイルの書式や内容に関する「最適な方法」をいくつか集めました。ここでの情報は、XMLファイルの内容をコントロールしていない場合にも役に立ちます。XMLファイルの書式化や更新の方法も、バージョン・コントロールの可能性に重大な影響を持つことがあるためです。

このトピックの対象読者は、チーム内で共有される、XMLベースのファイルを生成する機能の開発者です。

ルール1: 暗黙の変更を避ける

周知のとおり、ユーザーがエディタを使用して明示的にファイルを編集する場合以外は、ファイルの内容を変更しないでください。たとえば、選択されているプロジェクトをユーザーが変更するときや、XMLファイルに対してエディタを開くのみで実際には何も変更しないときに、ワークスペース・ファイルを変更しないでください。このルールは、実際には、XMLのみでなくすべてのファイル・タイプにあてはまります。

根拠

代替方法

ルール2: 変更が終わっていないときにファイルを「dirty」とマークすることは避ける

dirtyとマークすることは、実際にファイルの内容を変更していないときでも、ファイルが未保存に見えるようにすることをいいます。

根拠

ルール3: XMLファイル内の要素を勝手に並べ替えない

一部のXMLファイルは、内部データ構造に読み込まれた後、オブジェクトが保存されるときにXMLに書き戻されます。この場合、使用するたびに特定のデータ構造の順序が変化する可能性があることに留意してください。たとえば、要素をハッシュマップに格納する場合、マップの内容に応じてキーの順序が変化します。XMLファイル内の要素の順序への干渉を、常にできるだけ少なくするようにしてください。

根拠

ルール4: ユーザーが手動で編集できるXMLファイルは再書式化しない

ユーザーがテキスト・エディタを使用してXMLファイルを手動で編集することを許されている場合は、「自動」変更後にファイルを書き戻す(つまりビジュアル・エディタでXMLファイルを編集する)ときにユーザーの元の書式設定をできるだけ保持するようにします。

このルールは、コメント、属性の順序、インデント・サイズ、1行あたりのタブの数などを含むXMLファイルの書式設定全体にあてはまります。

例として、開発者が次のXMLファイルをCVSからチェックアウトするものとします。

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

<widget>
  <widget-instance><name>First Widget</name></widget-instance>
  <widget-instance>
    <name reskey="blah.name" resbundle="MyBundle" />
  </widget-instance>
</widget>    

このXMLファイルのビジュアル・エディタで最初のwidgetの名前を変更する場合、次のように書き出すのではなく、単に最初のwidgetの名前のみを変更する必要があります。

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

<!-- Generated by JDeveloper on 24-May-2004 -->
<widget>
   <widget-instance>
      <name>First Widget</name>
   </widget-instance>
   <widget-instance>
      <name resbundle="MyBundle" reskey="blah.name" />
   </widget-instance>
</widget>

    

このルールに対する明らかな例外は、ユーザーが(たとえばJDeveloperのXMLの自動インデント機能を使用して)明示的にXMLファイルの書式化を依頼する場合です。

根拠

ルール5: XML要素と属性は別々の行に分ける

XMLファイル内の各開始要素は、新しい行で始めてください。複数の属性を持つXML要素の場合、各属性を別々の行に分けます。

ファイル例:

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

<widget
    xmlns="http://xmlns.oracle.com/widgets"
    id="first.widget"
    class="FirstWidgetClass">

  <!-- This is a comment about widget instance -->
  <widget-instance>
    <!-- This is OK, on one line because there is only one attribute -->
    <subwidget id="sub.widget" />
    <!-- This one has two attributes, so is split up.
    <subwidget
        id="sub.widget.2"
        name="SubWidget2" 
    />
  </widget-instance>

</widget>

    

根拠

ルール6: マジック値は避ける

XMLは柔軟性の高い書式で、ほとんどどのような種類のデータでも表すことができます。ときには、コーディング効率の理由で、構造化データを別の書式でXMLファイル内に格納したいと考えることがあります。チーム開発の場合は、コードに必要な作業がほんの少し増えてXMLファイルが少し冗長になったとしても、XMLベースの表記のみを使用するほうがよい場合がほとんどです。

例として、最近まで、コードに次のようなビットマスクが使用されていました。

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

<method 
    name="doSomething"
    modifiers="97"
    returntype="void"
...
</method>

</widget>    

アクセス修飾子は、サブ要素または属性を使用して直接XMLにエンコードするほうがはるかによい方法です。たとえば次のようにします。

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

<method 
    name="doSomething"
    returntype="void"
  <modifiers>
    <public />
    <final />
    <synchronized />
  </modifiers>
...
</method>

</widget>    

または

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

<method 
    name="doSomething"
    returntype="void"
  <modifiers
      public="true" 
      final="true"
      synchronized="true" 
  />
...
</method>

</widget>    

もう1つの(悪い)例を示します。

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

<shape>
  <graphical-properties>
    <font>Bold|16|MS Sans Serif</font>
  </graphical-properties>
</shape>


    

根拠

ルール7: 要素をソートして競合を避ける

XMLファイルのなかには、ユーザーが作成できるオブジェクトのリストを含むものがあります。ユーザーが新規オブジェクトを作成するたびに、新規要素がXMLファイルの既存要素のセットの最後に追加されます。XMLファイル内でのこれらの要素の順序が重要でない場合、一部の属性またはサブ要素を使用してファイル内で要素をアルファベット順にソートすることを検討してください。

5つのwidgetを含むファイル例:

 1: <?xml version="1.0" encoding="UTF-8"?>
 2: 
 3: <widgets>
 4: 
 5:   <widget name="Sigroid" />
 6:   <widget name="Plasmoid" />
 7:   <widget name="Obeloid" />
 8:   <widget name="Turboid" />
 9:   <widget name="Tetroid" />
10: 
11: </widgets>    

2人の開発者が同時にKarmoidおよびRandomoidという名前の新規widgetを作成し、それらがリストの最後に挿入される場合、常に、行10に解決不能なマージ競合が発生します。

ただし、まずwidgetをソートして常に新規widgetをそれぞれのソート順序に挿入すると、両方の変更が競合なしで同時にマージできます。

 1: <?xml version="1.0" encoding="UTF-8"?>
 2: 
 3: <widgets>
 4: 
>>:   <widget name="Karmoid" />
 5:   <widget name="Obeloid" />
 6:   <widget name="Plasmoid" />
>>:   <widget name="Randomoid" />
 7:   <widget name="Sigroid" />
 8:   <widget name="Tetroid" />
 9:   <widget name="Turboid" />
10: 
11: </widgets>