Сериализация FeatureClass в XML в ESRI ArcGIS - PullRequest
1 голос
/ 04 мая 2010

Как я могу сериализовать объект IFeatureClass в XML?

Существуют некоторые ресурсы для использования IXMLSerializer на других объектах ArcObject, но это не сработает для IFeatureClass, поскольку он не реализует ISerializable.

1 Ответ

7 голосов
/ 04 мая 2010

Я действительно нашел свой ответ на этот вопрос. Я публикую этот вопрос и отвечу здесь для блага других и для обратной связи / критики моего подхода.

IFeatureClass не может быть сериализован напрямую, но IRecordSet2 может быть. Поэтому первым шагом является реализация метода для преобразования IFeatureClass в IRecordSet2:

private static IRecordSet2 ConvertToRecordset(IFeatureClass fc)
{
    IRecordSet recSet = new RecordSetClass();
    IRecordSetInit recSetInit = recSet as IRecordSetInit;
    recSetInit.SetSourceTable(fc as ITable, null);

    return (IRecordSet2) recSetInit;
}

Тогда легко использовать IXMLSerializer для получения XML:

public static XElement StoreAsXml(IFeatureClass fc)
{
    // Can't serialize a feature class directly, so convert
    //  to recordset first.
    IRecordSet2 recordset = ConvertToRecordset(fc);

    IXMLSerializer xmlSer = new XMLSerializerClass();
    string sXml = xmlSer.SaveToString(recordset, null, null);

    return XElement.Parse(sXml);           
}

Однако при преобразовании в IRecordSet2 вы теряете имя класса объектов, поэтому при записи в файл я добавляю элемент в XML для хранения имени класса объектов:

public static void StoreToFile(IFeatureClass fc, string filePath)
{
    XElement xdoc = StoreAsXml(fc);

    XElement el = new XElement("FeatureClass", new XAttribute( "name", fc.AliasName ),
                                xdoc);

    el.Save(filePath);
}

Теперь просто переверните процесс, чтобы прочитать XML в класс пространственных объектов. Помните, что для хранения имени класса пространственных объектов был добавлен элемент:

public static IFeatureClass RetreiveFromFile(string filepath)
{
    XElement xdoc = XElement.Load(filepath);
    string sName = xdoc.FirstAttribute.Value;
    XNode recordset = xdoc.FirstNode;

    return RetreiveFromXml(recordset, sName);
}

Простая десериализация с использованием IXMLSerializer для получения IRecordSet2:

public static IFeatureClass RetreiveFromXml(XNode node, string sName)
{
    IXMLSerializer xmlDeSer = new XMLSerializerClass();
    IRecordSet2 recordset = (IRecordSet2)xmlDeSer.LoadFromString(node.ToString(), null, null);

    return ConvertToFeatureClass(recordset, sName);
}

Это была сложная часть. Я открыт для предложений о том, как улучшить это ... преобразовать объект IRecordSet2 в IFeatureClass:

private static IFeatureClass ConvertToFeatureClass(IRecordSet2 rs, string sName)
{
    IWorkspaceFactory pWSFact = new ShapefileWorkspaceFactory();

    string sTempPath = Path.GetTempPath();
    IFeatureWorkspace pFWS = (IFeatureWorkspace)pWSFact.OpenFromFile( sTempPath, 0);

    // Will fail (COM E_FAIL) if the dataset already exists
    DeleteExistingDataset(pFWS, sName);

    IFeatureClass pFeatClass = null;
    pFeatClass = pFWS.CreateFeatureClass(sName, rs.Fields, null, null, esriFeatureType.esriFTSimple,
                                         "SHAPE", "");

    // Copy incoming record set table to new feature class's table
    ITable table = (ITable) pFeatClass;
    table = rs.Table;

    IFeatureClass result = table as IFeatureClass;

    // It will probably work OK without this, but it makes the XML match more closely
    IClassSchemaEdit3 schema = result as IClassSchemaEdit3;
    schema.AlterAliasName(sName);
    schema.AlterFieldAliasName("FID", "");
    schema.AlterFieldModelName("FID", "");
    schema.AlterFieldAliasName("Shape", "");
    schema.AlterFieldModelName("Shape", "");

    // If individual fields need to be edited, do something like this:
    //      int nFieldIndex = result.Fields.FindField("Shape");
    //      IFieldEdit2 field = (IFieldEdit2)result.Fields.get_Field(nFieldIndex);

    // Cleanup 
    DeleteExistingDataset(pFWS, sName);

    return table as IFeatureClass;
}

Наконец, служебный метод для удаления существующего набора данных. Это было скопировано / вставлено откуда-то, но я не могу вспомнить источник.

public static void DeleteExistingDataset(IFeatureWorkspace pFWS, string sDatasetName)
{
    IWorkspace pWS = (IWorkspace)pFWS;
    IEnumDatasetName pEDSN = pWS.get_DatasetNames(esriDatasetType.esriDTFeatureClass);
    bool bDatasetExists = false;
    pEDSN.Reset();
    IDatasetName pDSN = pEDSN.Next();
    while (pDSN != null)
    {
        if (pDSN.Name == sDatasetName)
        {
            bDatasetExists = true;
            break;
        }
        pDSN = pEDSN.Next();
    }
    if (bDatasetExists)
    {
        IFeatureClass pFC = pFWS.OpenFeatureClass(sDatasetName);
        IDataset pDataset = (IDataset)pFC;
        pDataset.Delete();
    }
}
...