ArcObjects SDK開發 025 AO中物件的序列化和反序列化

2023-01-04 18:00:40

在ArcObjects SDK,序列化介面是IPersistStream,該介面的定義如下。

截圖.png

其中GetClassID函數可以獲取實際型別的唯一ID,Load函數是反序列化函數,Save函數為序列化函數。我們看下Load和Save函數是接收什麼引數。

Save函數的定義如下所示。

public void Save (IStream pstm,int fClearDirty);
public void Load (IStream pstm);

Save函數傳入IStream介面型別的物件,第二個引數是否清空該物件的髒狀態。Load函數也是傳入一個IStream介面型別的物件。IStream的定義如下。

截圖.png

從幫助中看出只有一個XMLStream繼承實現了IStream介面,但實際操作的時候,儲存的xml字串會有亂碼,想把這些字串分序列化回去的時候,會報錯。這個問題估計是有其他辦法解決,但我一直沒有找到解決辦法,所以此路走不通。

實際上,除了XMLStream外,還有ESRI.ArcGIS.esriSystem.MemoryBlobStreamClass類也繼承IStream介面,但不知道為什麼幫助裡面沒有體現出來。從MemoryBlobStreamClass的命名也可以看出,使用這個類可以儲存成二進位制,也就是byte[],然後我們把byte[]轉換成字串就可以和其他資訊一起儲存成檔案或者xml中的一個節點了。

序列化程式碼如下。

public static string ToPersistString(IPersistStream pPersistStream)
{
    if (pPersistStream == null)
    {
        return "";
    }
    //得到ClassGuid
    pPersistStream.GetClassID(out Guid myClassGuid);
    string myClassGUID = myClassGuid.ToString();
    //得到二進位制資訊
    IMemoryBlobStream myMemoryBlobStream = new MemoryBlobStreamClass();
    pPersistStream.Save(myMemoryBlobStream, 0);
    IMemoryBlobStreamVariant myMemoryBlobStreamVariant = myMemoryBlobStream as IMemoryBlobStreamVariant;
    myMemoryBlobStreamVariant.ExportToVariant(out object myObject);
    return myClassGUID + "," + Convert.ToBase64String(myObject as byte[]);
}

需要注意的是,我們除了得到byte[]之外,還需要得到該物件所屬類的ClassGUID,通過IPersistStream的GetClassID函數可以獲取到,獲取之後,和byte[]一起儲存成字串。只有知道這段資訊儲存的是哪個類的範例,才能範例化出一個物件,並把該物件轉換成IPersistStream介面,最後調該介面的Load函數載入資訊。

反序列化的程式碼如下。

public static IPersistStream FromPersistString(string pPersistString)
{
    if (pPersistString.Trim().Length == 0)
    {
        return null;
    }
    string[] myPersistArray = pPersistString.Split(',');
    if (myPersistArray.Length < 1)
    {
        throw new ArgumentException("AoSerializer error.");
    }

    Type myType = Type.GetTypeFromCLSID(new Guid(myPersistArray[0]));
    IPersistStream myPersistStream = Activator.CreateInstance(myType) as IPersistStream;

    byte[] myByteArray = Convert.FromBase64String(myPersistArray[1]);
    IMemoryBlobStream myMemoryBlobStream = new MemoryBlobStreamClass();
    IMemoryBlobStreamVariant myMemoryBlobStreamVariant = myMemoryBlobStream as IMemoryBlobStreamVariant;
    myMemoryBlobStreamVariant.ImportFromVariant(myByteArray);
    myPersistStream.Load(myMemoryBlobStream);
    return myPersistStream;
}

反序列化程式碼首先先把ClassGUID和byte[]對應的字串分類,通過呼叫Type.GetTypeFromCLSID(new Guid(myPersistArray[0]));函數,可以獲取具體的型別,然後通過Activator.CreateInstance(myType)函數範例化一個該型別的物件,當然該物件肯定是繼承了IPersistStream介面的。

有了物件,有了資料,最後呼叫 myPersistStream.Load(myMemoryBlobStream);載入資料中的資訊,也就是把資料中儲存的資訊賦給呼叫的物件。

一般來說,我會把儲存的字串作為大xml檔案的一個屬性或者節點儲存,如下所示。

截圖.png

在第6行,定義了TextSymbol屬性,該屬性儲存了繼承ITextSymbol介面的物件,至於是哪個類,靠「,」號前面的的GUID字串去識別,「,」號後面就是儲存的具體資料。

第8行定義了MapSurroundFrame屬性,該屬性下儲存了一個IMapSurroundFrame介面型別的物件,解析方法和TextSymbol一樣。