如何将IFeatureClass对象序列化为XML?
在其他ArcObjects上使用IXMLSerializer有一些资源,但这对IFeatureClass不起作用,因为它没有实现ISerializable。
答案 0 :(得分:7)
我实际上找到了自己对这个问题的答案。我在这里发布这个问题和答案是为了他人的利益以及对我的方法的反馈/批评。
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();
}
}