IFeatureClass オブジェクトを XML にシリアル化するにはどうすればよいですか?
他の ArcObject で IXMLSerializer を使用するためのリソースがいくつかありますが、ISerializable を実装していないため、IFeatureClass では機能しません。
IFeatureClass オブジェクトを XML にシリアル化するにはどうすればよいですか?
他の ArcObject で IXMLSerializer を使用するためのリソースがいくつかありますが、ISerializable を実装していないため、IFeatureClass では機能しません。
私は実際にこの質問に対する独自の答えを見つけました。この質問と回答をここに投稿して、他の人の利益と、私のアプローチに対するフィードバック/批評を求めています。
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();
}
}