ArcGIS Engineにおけるオブジェクトの永続化とストリーム保存手法

ArcGIS Engine(AE)開発において、Layer、Element、Map、Legend、NorthArrowなどのオブジェクトをファイルやデータベースに保存したいケースは頻繁に発生します。FeatureLayerのFeatureはFeatureClassに直接保存できますが、GraphicsContainerに追加したElementについては標準的な保存機構が存在しないため、独自の永続化処理が必要になります。このような場合、オブジェクトをバイナリストリームやXML形式でシリアライズすることで、再利用可能な状態で保存・復元することが可能です。

シリアライズ対象となるオブジェクトは、IPersistStreamインターフェースを実装している必要があります。このインターフェースはWindows COM由来のもので、以下の4つのメソッドを通じてストリームとの入出力を行います:

メソッド機能
IsDirty()オブジェクトの状態が変更されたかを判定。保存が必要かどうかの判断材料
Load()ストリームからデータを読み込み、オブジェクトを復元
Save()オブジェクトの状態をストリームへ書き込み
GetSizeMax()必要なバッファサイズを事前に取得

バイナリ形式でのシリアライズ/デシリアライズ

/// <summary>
/// オブジェクトをバイト配列に変換
/// </summary>
public static byte[] SerializeToBytes(object target)
{
    if (!(target is IPersistStream streamable)) return null;

    var blob = new MemoryBlobStreamClass();
    var objStream = new ObjectStreamClass { Stream = blob };

    streamable.Save(objStream, 0);

    var size = (int)blob.Size;
    var buffer = new byte[size];
    ((IMemoryBlobStreamVariant)blob).ExportToVariant(out var data);
    return (byte[])data;
}

/// <summary>
/// バイト配列からオブジェクトを復元
/// </summary>
public static void DeserializeFromBytes(byte[] data, object target)
{
    if (!(target is IPersistStream streamable) || data == null) return;

    var blob = new MemoryBlobStreamClass();
    blob.ImportFromMemory(ref data[0], (uint)data.Length);

    var objStream = new ObjectStreamClass { Stream = blob };
    streamable.Load(objStream);
}

/// <summary>
/// Esri BLOBフィールド値をバイト配列に変換
/// </summary>
public static byte[] ConvertEsriBlob(object fieldValue)
{
    if (fieldValue == null || fieldValue == DBNull.Value) return null;

    var blob = (IMemoryBlobStream2)fieldValue;
    var size = (int)blob.Size;
    var buffer = new byte[size];
    ((IMemoryBlobStreamVariant)blob).ExportToVariant(out var data);
    return (byte[])data;
}

ファイルへの直接保存

/// <summary>
/// オブジェクトを指定パスのファイルに保存
/// </summary>
public static bool SaveObjectToFile(string filePath, object obj)
{
    if (!(obj is IPersistStream persistStream)) return false;

    var blobStream = new MemoryBlobStreamClass();
    var objectStream = new ObjectStreamClass { Stream = blobStream };

    persistStream.Save(objectStream, 0);
    try
    {
        blobStream.SaveToFile(filePath);
        return true;
    }
    catch
    {
        return false;
    }
}

XML形式によるワークスペース接続情報の保存と復元

/// <summary>
/// ワークスペース接続情報をXML文字列に変換
/// </summary>
public static string SerializeWorkspace(IWorkspace workspace)
{
    try
    {
        var xmlStream = new XMLStreamClass();
        var writer = new XMLWriterClass();
        writer.WriteTo(xmlStream as IStream);

        var serializer = new XMLSerializerClass();
        serializer.WriteObject(writer, null, null, "", "", workspace.ConnectionProperties);

        return xmlStream.SaveToString();
    }
    catch
    {
        return string.Empty;
    }
}

/// <summary>
/// XML文字列からワークスペースを復元
/// </summary>
public static IWorkspace DeserializeWorkspace(string xmlData, string password)
{
    try
    {
        var xmlStream = new XMLStreamClass();
        xmlStream.LoadFromString(xmlData);

        var reader = new XMLReaderClass();
        reader.ReadFrom(xmlStream as IStream);

        var serializer = new XMLSerializerClass();
        var propSet = serializer.ReadObject(reader, null, null) as IPropertySet;

        if (propSet != null)
        {
            propSet.SetProperty("PASSWORD", password);
            var factory = new SdeWorkspaceFactoryClass();
            return factory.Open(propSet, 0);
        }
    }
    catch
    {
        return null;
    }

    return null;
}

タグ: ArcGIS Engine IPersistStream シリアライズ COMインターフェース XMLSerializer

7月23日 17:21 投稿