ファイル格納処理および XML シリアル化処理をカスタマイズする

ユーザーが Visual Studio でドメイン固有言語 (DSL) のインスタンス、つまり "モデル" を保存すると、XML ファイルが作成または更新されます。 ファイルを再度読み込んで、ストアにモデルを再作成することができます。

DSL エクスプローラーで [XML シリアル化の動作] の設定を調整することで、シリアル化スキームをカスタマイズできます。 Xml シリアル化の動作には、すべてのドメイン クラス、プロパティ、リレーションシップのノードがあります。 リレーションシップは、ソース クラスの下に配置されます。 シェイプ、コネクタ、および図のクラスに対応するノードもあります。

さらに高度なカスタマイズのためのプログラム コードを記述することもできます。

注意

特定の形式でモデルを保存するものの、そのフォームから再度読み込む必要がない場合は、カスタムのシリアル化スキームではなく、テキスト テンプレートを使用してモデルからの出力を生成することを検討します。 詳細については、「ドメイン固有言語からのコード生成」を参照してください。

モデルと図のファイル

各モデルは、次の 2 つのファイルに保存されます。

  • モデル ファイルには、 Model1.mydslなどの名前があります。 モデル要素、リレーションシップ、およびそれらのプロパティが格納されます。 .mydslなどのファイル拡張子は、DSL 定義の Editor ノードの FileExtension プロパティによって決まります。

  • ダイアグラム ファイルには、 Model1.mydsl.diagramなどの名前があります。 シェイプ、コネクタ、その位置、色、線の太さなど、図の外観に関する詳細が格納されます。 ユーザーが .diagram ファイルを削除しても、モデル内の重要な情報は失われません。 図のレイアウトだけが失われます。 モデル ファイルを開くと、既定の図形とコネクタのセットが作成されます。

DSL のファイル拡張子を変更するには

  1. DSL 定義を開きます。 DSL エクスプローラーで、[エディター] ノードをクリックします。

  2. [プロパティ] ウィンドウで、FileExtension プロパティを編集します。 ファイル名拡張子の初期 . は含めないでください。

  3. ソリューション エクスプローラーで、DslPackage\ProjectItemTemplates 内にある 2 つの項目テンプレート ファイルの名前を変更します。 これらのファイルには、次の形式で名前が付いています。

    myDsl.diagram

    myDsl.myDsl

既定のシリアル化スキーム

このトピックの例を作成するために、次の DSL 定義を使用しました。

DSL 定義図 - ファミリ ツリー モデル

この DSL を使用して、画面上に次のような外観のモデルを作成しました。

ファミリ ツリー ダイアグラム、ツールボックス、およびエクスプローラー

このモデルは保存され、XML テキスト エディターで再度開かれます。

<?xml version="1.0" encoding="utf-8"?>
<familyTreeModel xmlns:dm0="http://schemas.microsoft.com/VisualStudio/2008/DslTools/Core" dslVersion="1.0.0.0" Id="f817b728-e920-458e-bb99-98edc469d78f" xmlns="http://schemas.microsoft.com/dsltools/FamilyTree">
  <people>
    <person name="Henry VIII" birthYear="1491" deathYear="1547" age="519">
      <children>
        <personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Elizabeth I" />
        <personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Mary" />
      </children>
    </person>
    <person name="Elizabeth I" birthYear="1533" deathYear="1603" age="477" />
    <person name="Mary" birthYear="1515" deathYear="1558" age="495" />
  </people>
</familyTreeModel>

シリアル化モデルについて、次の点に注意してください。

  • 各 XML ノードにはドメイン クラス名と同じ名前が付けられますが、最初の文字は小文字である点が異なります。 たとえば、familyTreeModelperson です。

  • Name や BirthYear などのドメイン プロパティは、XML ノードの属性としてシリアル化されます。 ここでも、プロパティ名の最初の文字が小文字に変換されます。

  • 各リレーションシップは、リレーションシップのソース エンド内で入れ子になった XML ノードとしてシリアル化されます。 ノードの名前は、ソース ロール プロパティと同じですが、小文字の先頭文字が使用されます。

    たとえば、DSL 定義では、People という名前のロールは FamilyTree クラスをソースとしています。 XML では、People ロールは、familyTreeModel ノード内に入れ子になったpeopleという名前のノードで表されます。

  • 各埋め込みリレーションシップのターゲット エンドは、リレーションシップの下で入れ子になったノードとしてシリアル化されます。 たとえば、people ノードに複数の person ノードが含まれているとします。

  • 各参照リレーションシップのターゲット エンドは、ターゲット要素への参照をエンコードする "モニカー" としてシリアル化されます。

    たとえば、person ノードの下に children リレーションシップが存在する場合があります。 このノードには、次のようなモニカーが含まれています。

    <personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Elizabeth I" />
    

モニカーについて

モニカーは、モデル ファイルと図ファイルの異なる部分の間の相互参照を表すために使用されます。 また、モデル ファイル内のノードを参照するために、 .diagram ファイルでも使用されます。 モニカーには 2 つの形式があります。

  • "ID モニカー" では、ターゲット要素の GUID を引用します。 次に例を示します。

    <personShapeMoniker Id="f79734c0-3da1-4d72-9514-848fa9e75157" />
    
  • "修飾キー モニカー" では、モニカー キーと呼ばれる指定されたドメイン プロパティの値によってターゲット要素を識別します。 ターゲット要素のモニカーには、埋め込みリレーションシップのツリー内の親要素のモニカーがプレフィックスとして付けられます。

    次の例は、Song という名前のドメイン クラスとの埋め込みリレーションシップを持つ Album という名前のドメイン クラスがある DSL から取得したものです。

    <albumMoniker title="/My Favorites/Jazz after Teatime" />
    <songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />
    

    修飾キー モニカーは、ターゲット クラスにドメイン プロパティがある場合に使用Is モニカー キーXml シリアル化動作trueに設定されます。 この例では、ドメイン クラス "Album" と "Song" でドメイン プロパティ "Title" に対してこのオプションが設定されています。

修飾キー モニカーは、ID モニカーよりも読みやすくなります。 モデル ファイルの XML を人間が判読できるようにする場合は、修飾キー モニカーの使用を検討してください。 ただし、ユーザーが同じモニカー キーを持つ複数の要素を設定することができます。 重複するキーが原因で、ファイルが正しく再度読み込みされない可能性があります。 したがって、修飾キー モニカーを使用して参照されるドメイン クラスを定義する場合は、重複するモニカーがあるファイルをユーザーが保存できないようにする方法を検討する必要があります。

ID モニカーによって参照されるようにドメイン クラスを設定するには

  1. クラスとその基本クラスのすべてのドメイン プロパティに対して [モニカー キー]false であることを確認します。

    1. DSL エクスプローラーで、[XML シリアル化の動作]\[クラス データ]\<ドメイン名>\[要素データ] を展開します。

    2. すべてのドメイン プロパティに対して [モニカー キー]false であることを確認します。

    3. ドメイン クラスに基本クラスがある場合は、そのクラスで手順を繰り返します。

  2. ドメイン クラスで [シリアル化 ID] = true を設定します。

    このプロパティは、 [XML シリアル化の動作] の下にあります。

修飾キー モニカーによって参照されるようにドメイン クラスを設定するには

  • 既存のドメイン クラスのドメイン プロパティに対して [モニカー キー] を設定します。 プロパティの型は string にする必要があります。

    1. DSL エクスプローラーで、[XML シリアル化の動作]\[クラス データ]\<ドメイン クラス>\[要素データ] を展開し、ドメイン プロパティを選択します。

    2. [プロパティ] ウィンドウで、 [モニカー キー]true に設定します。

  • - または -

    [名前付きドメイン クラス] ツールを使用して、新しいドメイン クラスを作成します。

    このツールでは、ドメイン プロパティ Name を持つ新しいクラスを作成します。 このドメイン プロパティの [要素名][モニカー キー] プロパティは true に初期化されます。

  • - または -

    ドメイン クラスから、モニカー キー プロパティを持つ別のクラスへの継承リレーションシップを作成します。

モニカーが重複しないようにする

修飾キー モニカーを使用する場合、ユーザーのモデル内の 2 つの要素がキー プロパティ内で同じ値を持つ可能性があります。 たとえば、DSL にプロパティ Name を持つクラス Person がある場合、ユーザーが 2 つの要素の Name を同じものに設定する可能性があります。 モデルをファイルに保存することはできますが、正しく再読み込みできません。

このような状況を回避するために、いくつかの方法があります。

  • キー ドメイン プロパティで [要素名] = true に設定します。 DSL 定義図でドメイン プロパティを選択し、プロパティウィンドウで値を設定します。

    ユーザーがクラスの新しいインスタンスを作成すると、この値が原因で、そのドメイン プロパティに別の値が自動的に割り当てられます。 既定の動作では、クラス名の末尾に数値が追加されます。 これにより、ユーザーは名前を重複に変更できなくなりますが、モデルを保存する前にユーザーが値を設定しない場合に役立ちます。

  • DSL の検証を有効にします。 DSL エクスプローラーで、[エディター]\[検証] を選択し、 [使用] プロパティを true に設定します。

    あいまいさをチェックする検証メソッドが自動的に生成されます。 このメソッドは、Load 検証カテゴリにあります。 これにより、ファイルを再度開くことはできませんという警告がユーザーに表示されます。

    詳細については、「ドメイン固有言語における検証」を参照してください。

モニカーのパスと修飾子

修飾キー モニカーは、モニカー キーで終了し、埋め込みツリーにおける親のモニカーがプレフィックスとして付けられます。 たとえば、Album のモニカーが次のようになっているとします。

<albumMoniker title="/My Favorites/Jazz after Teatime" />

このとき、その Album 内にある Song の 1 つは、次のようになります。

<songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />

ただし、Album が代わりに ID で参照される場合、モニカーは次のようになります。

<albumMoniker Id="77472c3a-9bf9-4085-976a-d97a4745237c" />
<songMoniker title="/77472c3a-9bf9-4085-976a-d97a4745237c/Hot tea" />

GUID は一意であるため、親のモニカーによってプレフィックスが付くことはありません。

特定のドメイン プロパティにモデル内で常に一意の値が使用されることがわかっている場合は、そのプロパティに対して [モニカー修飾子]true に設定できます。 これにより、親のモニカーを使用せずに修飾子として使用されます。 たとえば、Album クラスの Title ドメイン プロパティに対して Is モニカー修飾子Is モニカー キー の両方を設定した場合、モデルの名前または識別子は Album とその埋め込み子のモニカーでは使用されません。

<albumMoniker name="Jazz after Teatime" />
<songMoniker title="/Jazz after Teatime/Hot tea" />

XML 構造をカスタマイズする

次のカスタマイズを行うには、DSL エクスプローラーで [XML シリアル化の動作] ノードを展開します。 ドメイン クラスの下で、[要素データ] ノードを展開し、このクラスでソースとなるプロパティとリレーションシップの一覧を表示します。 リレーションシップを選択し、そのオプションを [プロパティ] ウィンドウで調整します。

  • ターゲット要素の一覧だけを残して、ソース ロール ノードを省略するには、 [要素の省略] を true に設定します。 ソース クラスとターゲット クラスの間に複数のリレーションシップがある場合は、このオプションを設定しないでください。

    <familyTreeModel ...>
      <!-- The following node is omitted by using Omit Element: -->
      <!-- <people> -->
        <person name="Henry VIII" .../>
        <person name="Elizabeth I" .../>
      <!-- </people> -->
    </familyTreeModel>
    
  • リレーションシップ インスタンスを表すノードにターゲット ノードを埋め込むには、 [完全なフォームを使用] を設定します。 ドメイン リレーションシップにドメイン プロパティを追加すると、このオプションは自動的に設定されます。

    <familyTreeModel ...>
      <people>
        <!-- The following node is inserted by using Use Full Form: -->
        <familyTreeModelHasPeople myRelationshipProperty="x1">
          <person name="Henry VIII" .../>
        </familyTreeModelHasPeople>
        <familyTreeModelHasPeople myRelationshipProperty="x2">
          <person name="Elizabeth I" .../>
        </familyTreeModelHasPeople>
      </people>
    </familyTreeModel>
    
  • 属性値としてではなく、要素として保存されたドメイン プロパティを持つには、 [表現] = [要素] に設定します。

    <person name="Elizabeth I" birthYear="1533">
      <deathYear>1603</deathYear>
    </person>
    
  • 属性およびリレーションシップをシリアル化する順序を変更するには、[要素データ] の下の項目を右クリックし、 [上へ移動] または [下へ移動] メニュー コマンドを使用します。

プログラム コードを使用した大規模なカスタマイズ

一部またはすべてのシリアル化アルゴリズムを置き換えることができます。

Dsl\Generated Code\Serializer.csSerializationHelper.cs のコードを学習することをお勧めします。

特定のクラスのシリアル化をカスタマイズするには

  1. [XML シリアル化の動作] の下で、そのクラスのノードの [カスタム] を設定します。

  2. すべてのテンプレートを変換し、ソリューションをビルドして、生成されたコンパイル エラーを調査します。 それぞれのエラーの近くにあるコメントでは、どのようなコードを提供する必要があるかを説明します。

モデル全体に独自のシリアル化を提供するには

  1. Dsl\GeneratedCode\SerializationHelper.cs でメソッドをオーバーライドします。

Note

Visual Studio 2022 17.13 以降では、既定のシリアル化の実装では、BinaryFormatter での セキュリティ 上のリスクがあるため、BinaryFormatter を使用したカスタム データ型のシリアル化または逆シリアル化はサポートされなくなりました

ドメイン プロパティにカスタム データ型を使用する場合は、 SerializationHelper クラスのシリアル化メソッドをオーバーライドするか、各カスタム データ型を文字列との間で変換できる TypeConverter を実装する必要があります。

セキュリティ上の理由から BinaryFormatter を使用することはお勧めしませんが、シリアル化を使用した古いモデルとの下位互換性 BinaryFormatter 維持する必要がある場合は、バイナリ データを逆シリアル化する TypeConverter を実装できます。 次のコード スニペットは、この互換性を実装するためのテンプレートとして機能します。

class MyCustomDataTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string text)
        {
            // First, try to parse the string as if it were returned by MyCustomDataType.ToString().
            if (MyCustomDataType.TryParse(text, out var custom))
                return custom;

            // Fall back to trying to deserialize the old BinaryFormatter serialization format.
            var decoded = Convert.FromBase64String(text);
            using (var memory = new MemoryStream(decoded, false))
            {
                var binaryFormatter = new BinaryFormatter();
                return binaryFormatter.Deserialize(memory) as MyCustomDataType;
            }
        }

        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string) && value is MyCustomDataType custom)
            return custom.ToString();

        return base.ConvertTo(context, culture, value, destinationType);
    }
}

// ...

[TypeConverter(MyCustomDataTypeConverter)]
class MyCustomDataType
{
    // ...
}

XML シリアル化の動作のオプション

DSL エクスプローラーの [Xml シリアル化動作] ノードには、各ドメイン クラス、リレーションシップ、図形、コネクタ、およびダイアグラム クラスの子ノードが含まれています。 これらの各ノードの下には、その要素でソースとなるプロパティとリレーションシップの一覧が表示されます。 リレーションシップは、独自の権限内とソース クラスの下の両方で表現されます。

次の表は、DSL 定義のこのセクションで設定できるオプションをまとめたものです。 どちらの場合も、DSL エクスプローラーで要素を選択し、[プロパティ] ウィンドウでオプションを設定します。

XML クラス データ

これらの要素は、DSL エクスプローラーの [XML シリアル化の動作]\[クラス データ] にあります。

プロパティ 説明
カスタム要素スキーマを含む True の場合、ドメイン クラスにカスタム要素スキーマがあることを示します。
カスタム このドメイン クラスの独自のシリアル化および逆シリアル化コードを記述する場合は値を True に設定します。

ソリューションをビルドし、エラーを調査して、詳細な手順を見つけます。
ドメイン クラス このクラス データ ノードが適用されるドメイン クラス。 読み取り専用です。
要素名 このクラスの要素の XML ノード名。 既定値は、ドメイン クラス名の小文字バージョンです。
モニカー属性名 参照を格納するためにモニカー要素で使用される属性の名前。 空白の場合は、キー プロパティまたは ID の名前が使用されます。

この例では、"name" です。 <personMoniker name="/Mike Nash"/>
モニカー要素名 このクラスの要素を参照するモニカーに使用する XML 要素の名前。

既定値は、"Moniker" というサフィックスが付けられた、クラス名の小文字バージョンです。 たとえば、「 personMoniker 」のように入力します。
モニカーの型名 このクラスの要素に対してモニカー用に生成される XSD 型の名前。 XSD は Dsl\Generated Code\*Schema.xsd にあります
シリアル化 ID True の場合、要素の GUID はファイルに含まれます。 値 must True に設定されます is モニカー キー マークされているプロパティがなく DSL がこのクラスへの参照リレーションシップを定義する場合です。
種類名 指定したドメイン クラスからの XSD で生成される XML 型の名前。
Notes この要素に関連付けられた非公式のメモ。

XML プロパティ データ

XML プロパティ ノードは、クラス ノードの下にあります。

プロパティ 説明
ドメイン プロパティ XML シリアル化の構成データが適用されるプロパティ。 読み取り専用です。
モニカー キー 値が True に設定されている場合、このプロパティは、このドメイン クラスのインスタンスを参照するモニカーを作成するためのキーとして使用されます。
モニカー修飾子 値が True に設定されている場合、このプロパティはモニカーで修飾子を作成するために使用されます。 false の場合、およびこのドメイン クラスに対して SerializeId が true でない場合、モニカーは埋め込みツリー内の親要素のモニカーによって修飾されます。
[表記] 値が Attribute に設定されている場合、プロパティは xml 属性としてシリアル化されます。値が Element に設定されている場合は、要素としてシリアル化されます。値が Ignore に設定されている場合は、シリアル化されません。
XML 名 プロパティを表す XML 属性または要素に使用される名前。 既定では、値はドメイン プロパティ名の小文字のバージョンです。
メモ この要素に関連付けられた非公式のメモ。

XML ロール データ

ロール データ ノードは、ソース クラス ノードの下にあります。

プロパティ 説明
カスタム モニカーを含む このリレーションシップを走査するモニカーを生成および解決するための独自コードを指定する場合は、これを true に設定します。

詳細な手順については、ソリューションをビルドし、エラー メッセージをダブルクリックしてください。
ドメイン リレーションシップ これらのオプションが適用されるリレーションシップを指定します。 読み取り専用です。
要素の省略 true の場合、ソース ロールに対応する XML ノードはスキーマから省略されます。

ソース クラスとターゲット クラスの間に複数のリレーションシップがある場合、このロール ノードは、2 つのリレーションシップに属するリンクを区別します。 そのため、この場合はこのオプションを設定しないことをお勧めします。
ロール要素名 ソース ロールから派生した XML 要素の名前を指定します。 既定値は、ロール プロパティ名です。
完全なフォームを使用 true の場合、各ターゲット要素またはモニカーは、リレーションシップを表す XML ノードで囲まれます。 リレーションシップに独自のドメイン プロパティがある場合は、これを true に設定する必要があります。