自訂檔案儲存體和 XML 序列化
當使用者在 Visual Studio 中儲存特定領域語言 (DSL) 的執行個體或模型時,就會建立或更新 XML 檔案。 檔案可以重新載入以在市集中重新建立模型。
您可以藉由在 DSL 總管中的 XML 序列化行為下調整設定,自訂序列化配置。 每個網域類別、屬性和關聯性都有 Xml 串行化行為下的節點。 關聯性位於其來源類別底下。 也有對應至圖形、連接器和圖表類別的節點。
您也可以撰寫程式碼以進行更進階的自訂。
注意
如果您想要以特定格式儲存模型,但不需要從該表單重新載入模型,請考慮使用文字範本從模型產生輸出,而不是自訂序列化配置。 如需詳細資訊,請參閱從特定領域語言產生程式碼。
模型和圖表檔案
每個模型都會儲存在兩個檔案中:
模型檔案的名稱如下
Model1.mydsl
。 會儲存模型元素和關聯性及其屬性。 例如的.mydsl
擴展名是由 DSL 定義中 Editor 節點的 FileExtension 屬性所決定。圖表檔案的名稱如下
Model1.mydsl.diagram
。 會儲存圖形、連接器及其位置、色彩、線條粗細,以及圖表外觀的其他詳細資料。 如果使用者刪除檔案.diagram
,則模型中的基本資訊不會遺失。 只有圖表配置會遺失。 開啟模型檔案時,會建立一組預設的圖形和連接器。
變更 DSL 副檔名
開啟 DSL 定義。 在 [DSL 總管] 中,按一下 [編輯器] 節點。
在 [屬性] 視窗中,編輯 FileExtension 屬性。 請勿包含擴展名的縮寫
.
。在 [方案總管] 中,變更 DslPackage\ProjectItemTemplates 中兩個項目範本檔案的名稱。 這些檔案具有遵循以下格式的名稱:
myDsl.diagram
myDsl.myDsl
預設序列化配置
為了建立此主題的範例,使用了下列 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 節點都具有與領域類別名稱相同的名稱,但是初始字母為小寫。 例如,
familyTreeModel
與person
。Name 和 BirthYear 等領域屬性會序列化為 XML 節點中的屬性。 同樣地,屬性名稱的初始字元會轉換成小寫。
每個關聯性都會序列化為在關聯性來源端內成為巢狀的 XML 節點。 節點具有與來源角色屬性相同的名稱,但是初始字元為小寫。
例如,在 DSL 定義中,People 的角色其來源為 FamilyTree 類別。 在 XML 中,People 角色是以節點內
familyTreeModel
巢狀的people
節點來表示。每個內嵌關聯性的目標端都會序列化為在關聯性下巢狀的節點。 例如,
people
節點包含數個person
節點。每個參考關聯性的目標端都會序列化為 Moniker,將參考編碼為目標元素。
例如,在
person
節點下,可能會有children
關聯性。 此節點包含 Moniker,例如:<personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Elizabeth I" />
了解 Moniker
Moniker 是用來代表模型與圖表檔案的不同部分之間的交叉參考。 它們也會用於 檔案中 .diagram
,以參考模型檔案中的節點。 Moniker 有兩種形式:
識別碼 Moniker 會引用目標元素的 GUID。 例如:
<personShapeMoniker Id="f79734c0-3da1-4d72-9514-848fa9e75157" />
限定索引鍵 Moniker 會以名為 Moniker 索引鍵的指定領域屬性值識別目標元素。 目標專案的Moniker前面會加上內嵌關聯性樹狀結構中其父元素的Moniker。
下列範例取自 DSL,其中有名為 Album 的網域類別,其與名為 Song 的網域類別有內嵌關聯性:
<albumMoniker title="/My Favorites/Jazz after Teatime" /> <songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />
如果目標類別具有定義域屬性,則會使用限定索引鍵Moniker索引鍵在 Xml 串行化行為中設定為
true
的網域屬性。 在此範例中,針對領域類別 "Album" 和 "Song" 中名為 "Title" 的領域屬性設定此選項。
限定索引鍵 Moniker 比識別碼 Moniker 更容易讀取。 如果您想要讓模型檔案的 XML 成為人類可讀取的,請考慮使用合格的密鑰 Moniker。 不過,用戶可以將多個元素設定為具有相同Moniker索引鍵。 重複的索引鍵可能會導致檔案無法正確重新載入。 因此,如果您定義使用限定索引鍵 Moniker 參考的領域類別,您應該考慮防止使用者儲存具有重複 Moniker 的檔案。
設定識別碼 Moniker 所參考的領域類別
請確定 [Is Moniker Key] 是類別及其基底類別中每個領域屬性的
false
。在 [DSL 總管] 中,展開 Xml Serialization Behavior\Class Data\<領域類別>\Element Data。
確認每個領域屬性的 [是 Moniker 索引鍵] 是
false
。如果領域類別有基底類別,請重複該類別中的程序。
為領域類別設定 Serialize Id =
true
。您可以在 [XML 序列化行為] 底下找到此屬性。
設定限定索引鍵 Moniker 所參考的領域類別
為現有領域類別的領域屬性設定 [是 Moniker 索引鍵]。 屬性的類型必須是
string
。在 [DSL 總管] 中,展開 Xml Serialization Behavior\Class Data\<領域類別>\Element Data,然後選取領域屬性。
在 [屬性] 視窗中,將 [Is Moniker Key] 設定為
true
。
- 或 -
使用具名領域類別工具建立新的領域類別。
此工具會建立名為 Name 之領域屬性的新類別。 此領域屬性的 [Is Element Name] 和 [Is Moniker Key] 屬性會初始化為
true
。- 或 -
建立從領域類別到具有 Moniker 索引鍵屬性的另一個類別的繼承關聯性。
避免重複 Moniker
如果您使用合格的索引鍵 Monikers,則使用者模型中的兩個元素在索引鍵屬性中可能會有相同的值。 例如,如果您的 DSL 類別 Person 具有屬性 Name,則使用者可以將兩個元素的 Name 設定為相同。 雖然模型可以儲存至檔案,但不會正確重載。
有數種方法可協助避免這種情況:
為索引鍵領域屬性設定 [Is Element Name] =
true
。 選取 DSL 定義圖表上的領域屬性,然後在 [屬性] 視窗中設定值。當使用者建立類別的新執行個體時,這個值會導致領域屬性自動獲指派不同的值。 預設行為會將數字新增至類別名稱的結尾。 這不會防止使用者將名稱變更為重複名稱,但在儲存模型之前,使用者未設定值的情況會有所説明。
啟用 DSL 的驗證。 在 [DSL 總管] 中,選取 Editor\Validation,然後將 [Uses...] 屬性設定為
true
。有自動產生的驗證方法會檢查模棱兩可。 方法位於
Load
驗證類別中。 這可確保使用者會被警告,可能無法重新開啟檔案。如需詳細資訊,請參閱特定領域語言中的驗證。
Moniker 路徑和限定詞
限定索引鍵 Moniker 結尾為 Moniker 索引鍵,並在內嵌樹狀結構中加上其父系的 Moniker 前置詞。 例如,如果 Album 的 Moniker 為:
<albumMoniker title="/My Favorites/Jazz after Teatime" />
然後該 Album 中的其中一首 Song 是:
<songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />
不過,如果系統會改為以識別碼參考 Album,則 Moniker 會如下所示:
<albumMoniker Id="77472c3a-9bf9-4085-976a-d97a4745237c" />
<songMoniker title="/77472c3a-9bf9-4085-976a-d97a4745237c/Hot tea" />
請注意,因為 GUID 是唯一的,所以永遠不會加上其父系的 Moniker 前置詞。
如果您知道特定領域屬性在模型中一律會有唯一值,您可以針對該屬性將 [Is Moniker Qualifier] 設定為 true
。 這會導致它當做限定符使用,而不要使用父系的Moniker。 例如,如果您同時 為 Album 類別的 Title 網域屬性設定 Is Moniker 限定 符和 Is Moniker Key ,則模型的名稱或標識符不會用於 Album 的 Moniker 及其內嵌子系:
<albumMoniker name="Jazz after Teatime" />
<songMoniker title="/Jazz after Teatime/Hot tea" />
自訂 XML 的結構
若要進行下列自訂,請展開 [DSL 總管] 中的 [Xml Serialization Behavior] 節點。 在領域類別下,展開 [Element Data] 節點,以查看來源為此類別的屬性和關聯性清單。 選取關聯性,並在 [屬性] 視窗中調整其選項。
將 [Omit Element] 設定為 true,以省略來源角色節點,僅留下目標元素的清單。 如果來源和目標類別之間有多個關聯性,則不應該設定此選項。
<familyTreeModel ...> <!-- The following node is omitted by using Omit Element: --> <!-- <people> --> <person name="Henry VIII" .../> <person name="Elizabeth I" .../> <!-- </people> --> </familyTreeModel>
設定 [Use Full Form],將目標節點內嵌在代表關聯性執行個體的節點中。 當您將領域屬性新增至領域關聯時,會自動設定此選項。
<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.cs 和 SerializationHelper.cs 中的程式碼。
自訂特定類別的序列化
在 [Xml Serialization Behavior] 底下該類別的節點中設定 [Is Custom]。
轉換所有範本、建置解決方案,並調查產生的編譯錯誤。 每個錯誤附近的註解會說明您必須提供的程式碼。
為整個模型提供您自己的序列化
- 覆寫 Dsl\GeneratedCode\SerializationHelper.cs 中的方法
注意
從 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 Serialization Behavior\Class Data 底下找到。
屬性 | 說明 |
---|---|
Has Custom Element Schema | 如果為 True,指出領域類別有自訂元素結構描述 |
是自訂 | 如果您想要撰寫這個網域類別的串行化和還原串行化程式代碼,請將值 設定為 True 。 建置解決方案並調查錯誤,以探索詳細指示。 |
領域類別 | 套用此類別資料節點的領域類別。 唯讀。 |
元素名稱 | 這個類別元素的 XML 節點名稱。 預設值是領域類別名稱的小寫版本。 |
Moniker Attribute Name | Moniker 元素中用來包含參考的屬性名稱。 如果空白,則會使用索引鍵屬性或識別碼的名稱。 在此範例中,其為 「name」: <personMoniker name="/Mike Nash"/> |
Moniker Element Name | XML 元素名稱,用於參考此類別元素的 Moniker。 預設值是尾碼為「Moniker」的小寫版本類別名稱。 例如: personMoniker 。 |
Moniker Type Name | XSD 類型名稱,替此類型元素的 Moniker 所產生。 XSD 位於 Dsl\Generated Code\*Schema.xsd |
Serialize Id | 如果為 True,則元素 GUID 會包含在檔案中。 如果沒有標示為Moniker Key的屬性,且DSL定義這個類別的參考關聯性,則必須將值設定為True。 |
類型名稱 | XML 類型名稱,自指定的領域類別中的 XSD 產生。 |
備註 | 與此元素相關的非正式備註 |
Xml Property Data
XML 屬性節點位於類別節點底下。
屬性 | 說明 |
---|---|
領域屬性 | 套用 XML 序列化組態資料的屬性。 唯讀。 |
Is Moniker Key | 如果值設定為 True,則會使用 屬性做為索引鍵,用來建立參考這個網域類別實例的 Moniker。 |
Is Moniker Qualifier | 如果值設定為 True,則會使用 屬性在 Moniker 中建立限定符。 如果為 false,而且如果此網域類別的 SerializeId 不是 true,Monikers 就會由內嵌樹狀結構中父元素的 Moniker 限定。 |
表示法 | 如果值設定為 Attribute,則屬性會串行化為 xml 屬性;如果值設定為 Element,則會將其串行化為元素;如果值設定為 Ignore,則不會串行化。 |
Xml Name | 名稱,用於 XML 屬性或代表屬性的元素。 根據預設,此值是網域屬性名稱的小寫版本。 |
備註 | 與此元素相關的非正式備註 |
Xml Role data
角色資料節點位於來源類別節點底下。
屬性 | 說明 |
---|---|
Has Custom Moniker | 如果您想要提供自己的程式碼來產生和解析周遊此關聯性的 Moniker,請將此屬性設定為 true。 如需詳細指示,請建置解決方案,然後按兩下錯誤訊息。 |
領域關聯 | 指定這些選項套用的關聯性。 唯讀。 |
Omit Element | 若為 True,會從結構描述中省略對應到來源角色的 XML 節點。 如果來源和目標類別之間有多個關聯性,此角色節點會區分屬於兩個關聯性的連結。 因此,建議您在此案例中不要設定此選項。 |
Role Element Name | 指定衍生自來源角色的 XML 元素名稱。 預設值是角色屬性名稱。 |
Use Full Form | 如果為 true,則每個目標元素或 Moniker 都會包含在代表關聯性的 XML 節點中。 如果關聯性有自己的領域屬性,這應該設定為 true。 |