TopoJSON 是地理資料的緊湊編碼,且在 TopoJSON 檔案中,每個特徵通常會攜帶一個屬性物件,而不是平面的鍵值對集合。將此結構匯出為 CSV 時,往往會出現問題:大多數工具要麼會丟棄嵌套物件,要麼會把它作為 JSON 字串塞入單一儲存格。本指南展示了 如何在 C# 中使用 Aspose.GIS 將 TopoJSON 轉換為 CSV,將每個屬性保留為可單獨尋址的欄位,並可選擇將幾何資訊以 Well‑Known Text 形式寫入。
好處在於,層次化的 GIS 資料集會變成一個平面表格,SQL Server、Power BI、pandas 或普通的試算表都能直接使用,且您的應用程式中不需要自訂的 JSON 解析層。
為什麼使用 Aspose.GIS 將 TopoJSON 轉換為 CSV
考慮一個 TopoJSON 特徵,其屬性看起來像這樣:
{
"name": "North District",
"prop1": { "this": "that" }
}
一個天真的 CSV 寫入器有三個不好的選擇:丟棄 prop1、將其序列化為 {"this":"that"} 放入單一儲存格,或失敗。這三種方式都會把問題推向下游,必須在有人能在文字欄位中重新解析 JSON 後,才能對其進行篩選或連接。
Aspose.GIS 採取了不同的方法。當 TopoJSON 驅動程式讀取檔案時,它會將巢狀物件投射到圖層結構中,作為一個以其路徑命名的平面屬性,將父層與子層以底線連接。上述特徵會以兩個普通欄位的形式顯示:
| 欄位 | 值 |
|---|---|
name | 北區 |
prop1_this | 那 |
因為扁平化在讀取時發生,轉換步驟根本不需要任何特殊處理。層所暴露的內容就是寫入的內容,因此 prop1_this 會作為第一級欄位出現在 CSV 中,您可以對其進行排序、篩選和連接。
TopoJSON 轉 CSV 轉換的先決條件
- .NET 6.0 或更新版本,或 .NET Framework 4.6.2 或更新版本
- Aspose.GIS for .NET,已從 NuGet 安裝
- 包含至少一個嵌套屬性物件的 TopoJSON 檔案
使用 .NET CLI 安裝套件:
dotnet add package Aspose.GIS
或從套件管理員控制台:
Install-Package Aspose.GIS
有一個命名細節會讓人感到困惑:NuGet 套件和組件名稱為 Aspose.GIS,但根命名空間是 Aspose.Gis,其中的 is 為小寫。CSV 驅動程式的選項位於 Aspose.Gis.Formats.Csv。
using Aspose.Gis;
using Aspose.Gis.Formats.Csv;
產品詳細資訊位於 Aspose.GIS for .NET 產品頁面,完整類型清單請參閱 API 參考文件。
在轉換之前檢查已展平的結構
您將完成的工作: 打開 TopoJSON 圖層並列印其屬性名稱,以便您確切了解 CSV 將包含哪些欄位。
此步驟值得對一個具代表性的檔案執行一次。它會告訴您透過展平產生的實際欄位名稱,這比從原始 JSON 猜測要可靠得多。
using System;
using Aspose.Gis;
using (VectorLayer layer = VectorLayer.Open("data/regions.topojson", Drivers.TopoJson))
{
Console.WriteLine($"Features: {layer.Count}");
foreach (FeatureAttribute attribute in layer.Attributes)
{
Console.WriteLine($" {attribute.Name} ({attribute.DataType})");
}
}
對於前面顯示的示例功能,這會列印 name (String) 和 prop1_this (String)。嵌套對象永遠不會作為獨立的屬性出現,這正是您想要的。
將 TopoJSON 轉換為 CSV 並保留嵌套屬性
您將完成的工作: 載入 TopoJSON 檔案,將每個展平的屬性寫入 CSV 檔,然後讀取結果以確認欄位已正確傳遞。
逐步流程
- 定義來源 TopoJSON 路徑和目標 CSV 路徑。
- 使用
VectorLayer.Open開啟來源,並確認已展平的屬性存在。 - 使用來源和目標驅動程式呼叫靜態
VectorLayer.Convert重載。 - 使用
Drivers.Csv重新開啟 CSV,並檢查欄位數量和數值。
以下範例將 TopoJSON 檔案轉換為 CSV,並在此過程中展平巢狀屬性。
using System;
using System.IO;
using Aspose.Gis;
class TopoJsonToCsv
{
static void Main()
{
string sourcePath = Path.Combine("data", "regions.topojson");
string destinationPath = Path.Combine("output", "regions.csv");
Directory.CreateDirectory("output");
// Confirm the nested property was flattened when the layer was opened.
using (VectorLayer source = VectorLayer.Open(sourcePath, Drivers.TopoJson))
{
Console.WriteLine($"Source features: {source.Count}");
Console.WriteLine($"Has prop1_this: {source.Attributes.Contains("prop1_this")}");
}
// No options are needed. The conversion writes whatever columns the
// source layer exposes, flattened attributes included.
VectorLayer.Convert(sourcePath, Drivers.TopoJson, destinationPath, Drivers.Csv);
// Read the CSV back and verify the schema survived the round trip.
using (VectorLayer csv = VectorLayer.Open(destinationPath, Drivers.Csv))
{
Console.WriteLine($"Rows: {csv.Count}, Columns: {csv.Attributes.Count}");
if (csv.Attributes.Contains("prop1_this"))
{
Console.WriteLine($"First value: {csv[0].GetValue<string>("prop1_this")}");
}
// No WKT column was requested, so features read back without geometry.
Console.WriteLine($"Geometry empty: {csv[0].Geometry.IsEmpty}");
}
}
}
說明
VectorLayer.Open(string, FileDriver)以唯讀方式開啟檔案。Drivers.TopoJson回傳 TopoJSON 格式的驅動程式。FeatureAttributeCollection.Contains(string)是測試是否存在指定欄位名稱的最簡便方法。若需要欄位位置,可使用IndexOf取得其索引。VectorLayer.Convert(string, FileDriver, string, FileDriver)是一個靜態輔助方法,可在一次呼叫中將來源資料串流轉換為目標格式。另有接受ConversionOptions的重載,於下一節中使用。Feature.GetValue<T>(string)讀取具型別的屬性值。當欄位可能不存在或為 null 時,請使用GetValueOrDefault<T>(string),因為GetValue<T>假設該值一定存在。- 最後的檢查往往讓人感到意外。CSV 檔案本身沒有幾何欄位,除非自行建立;因此每個特徵都會返回空的幾何資訊。這是預設行為,並非轉換失敗,下一節將說明如何解決。
在此情況下,將兩個層級都包裹在 using 區塊中很重要:VectorLayer 實作了 IDisposable,而且在釋放之前,無法保證目標檔案已被刷新並關閉。
使用 CsvOptions 包含幾何作為 WKT
您將完成的目標: 產生一個 CSV,該 CSV 同時包含展平的屬性 以及 一個保存每個要素幾何形狀的 Well-Known Text 列。
逐步流程
- 建立一個
CsvOptions實例,並將ColumnWkt設定為您選擇的欄位名稱。 - 將其指派給
ConversionOptions.DestinationDriverOptions。 - 呼叫接受轉換選項的
VectorLayer.Convert重載。 - 使用相同的
CsvOptions重新開啟 CSV,讓驅動程式知道要將哪個欄位解析為幾何圖形。
using System;
using System.IO;
using Aspose.Gis;
using Aspose.Gis.Formats.Csv;
class TopoJsonToCsvWithGeometry
{
static void Main()
{
string sourcePath = Path.Combine("data", "regions.topojson");
string destinationPath = Path.Combine("output", "regions-wkt.csv");
Directory.CreateDirectory("output");
// Ask the CSV driver for a geometry column named "GeometryWkt".
var csvOptions = new CsvOptions
{
ColumnWkt = "GeometryWkt"
};
var conversionOptions = new ConversionOptions
{
DestinationDriverOptions = csvOptions
};
VectorLayer.Convert(
sourcePath,
Drivers.TopoJson,
destinationPath,
Drivers.Csv,
conversionOptions);
// Pass the same options when reading, or the WKT column is just text.
using (VectorLayer layer = VectorLayer.Open(destinationPath, Drivers.Csv, csvOptions))
{
foreach (Feature feature in layer)
{
string name = feature.GetValueOrDefault<string>("name");
string wkt = feature.Geometry.IsEmpty ? "(empty)" : feature.Geometry.AsText();
Console.WriteLine($"{name}: {wkt}");
}
}
}
}
說明
CsvOptions.ColumnWkt命名了保存每個要素幾何形狀(以 WKT 形式)的欄位。預設為null,將其保持為 null 或空值正是導致先前範例未產生幾何形狀的原因。ConversionOptions.DestinationDriverOptions的類型為DriverOptions,它是所有驅動程式設定物件的基礎類別。將CsvOptions傳遞給目的驅動程式為Drivers.Csv時,設定才會生效;若為驅動程式傳遞錯誤類型的選項,會拋出ArgumentException。- 同一個
CsvOptions實例在讀取端會被重複使用。若沒有它,CSV 驅動程式就沒有理由將GeometryWkt視為除另一個字串欄位之外的其他類型。 IGeometry.AsText()會回傳 ISO WKT 表示形式,因此列印的輸出與寫入檔案的內容相符。
對稱性是關鍵:屬性欄位在是否包含幾何資訊時表現相同,因此您可以在不影響管道其他部分的情況下,開啟或關閉 WKT 欄位。
在轉換期間重新投影
如果消費系統需要特定的坐標系統,請在轉換過程中提出需求,而不是在 CSV 之後進行後處理。ConversionOptions 會攜帶空間參考以及驅動程式選項:
var conversionOptions = new ConversionOptions
{
DestinationDriverOptions = new CsvOptions { ColumnWkt = "GeometryWkt" },
DestinationSpatialReferenceSystem = SpatialReferenceSystem.Wgs84
};
VectorLayer.Convert(sourcePath, Drivers.TopoJson, destinationPath, Drivers.Csv, conversionOptions);
並非所有目標驅動程式都支援每個空間參考系統。如果目標系統可在執行時配置,請先呼叫 Drivers.Csv.SupportsSpatialReferenceSystem(...),若不支援則回退到 null 選項物件。
批次轉換資料夾中的 TopoJSON 檔案
API 在處理單個檔案和千個檔案時是相同的。當您平行化時,唯一值得記住的規則是每個工作者都需要自己的層和選項物件。
using System;
using System.IO;
using System.Threading.Tasks;
using Aspose.Gis;
using Aspose.Gis.Formats.Csv;
static void ConvertFolder(string inputFolder, string outputFolder)
{
Directory.CreateDirectory(outputFolder);
var files = Directory.GetFiles(inputFolder, "*.topojson");
Parallel.ForEach(files, file =>
{
string destination = Path.Combine(
outputFolder,
Path.GetFileNameWithoutExtension(file) + ".csv");
// Fresh options per iteration: nothing is shared across threads.
var options = new ConversionOptions
{
DestinationDriverOptions = new CsvOptions { ColumnWkt = "GeometryWkt" }
};
try
{
VectorLayer.Convert(file, Drivers.TopoJson, destination, Drivers.Csv, options);
}
catch (GisException ex)
{
Console.Error.WriteLine($"Skipped {Path.GetFileName(file)}: {ex.Message}");
}
});
}
對每個檔案捕獲 GisException 而不是在整個迴圈周圍捕獲,意味著單個格式錯誤的 TopoJSON 檔案只會記錄警告,而不會中止批次。
取得免費授權
Aspose.GIS 在應用許可證之前會以評估模式運行。請從 Aspose 臨時許可證頁面 申請免費的臨時許可證,並在應用程式啟動時一次性套用,於任何其他 Aspose.GIS 呼叫之前執行:
var license = new Aspose.Gis.License();
license.SetLicense("Aspose.GIS.NET.lic");
免費的額外資源
結論
在 .NET 中將具有巢狀屬性的 TopoJSON 轉換為 CSV,核心在於一個有用的行為:Aspose.GIS 在讀取圖層時會將巢狀屬性物件展平為具名欄位,例如 prop1_this,因此 VectorLayer.Convert 不需要自訂邏輯來保留它們。當下游系統需要幾何資訊時加入 CsvOptions.ColumnWkt,當需要特定投影時加入 DestinationSpatialReferenceSystem,並在需要處理資料夾時重複呼叫同一方法。最終產生的是一個平坦、可直接分析的表格,只需少量 C# 程式碼,且無需自行維護 JSON 解析層。
常見問題
Aspose.GIS 在轉換為 CSV 時能否展平嵌套的 JSON 物件? 是的。TopoJSON 驅動程式在打開圖層時,會將嵌套的屬性物件顯示為獨立的欄位,並以底線將父層與子層名稱連接起來,因此在執行轉換之前,展平已經完成。
我需要編寫自訂程式碼來處理巢狀屬性嗎? 不需要。
VectorLayer.Convert會寫入來源圖層所公開的任何欄位,因此巢狀屬性不需要您自行額外的解析程式碼。如何在 CSV 輸出中包含幾何資訊? 建立一個
CsvOptions實例,將其ColumnWkt屬性設定為您想要的欄位名稱,將其指派給ConversionOptions.DestinationDriverOptions,然後將其傳遞給VectorLayer.Convert。為什麼在重新讀取 CSV 時幾何資訊是空的? CSV 檔案本身沒有幾何欄位,除非你特別要求。請在寫入和讀取時傳遞的
CsvOptions上設定ColumnWkt,否則驅動程式將沒有欄位可用來建立幾何資訊。在轉換過程中應該處理哪些例外? 文件中列出的例外包括
ArgumentNullException(當路徑為 null 時),ArgumentException(當選項物件與驅動程式不匹配時),GisException(當無法從檔案中讀取特徵時),以及IOException(底層 I/O 失敗時)。我可以一次性轉換一批 TopoJSON 檔案嗎? 是的。遍歷檔案清單,對每個檔案呼叫
VectorLayer.Convert。如果將迴圈平行化,請為每個工作執行緒提供各自的VectorLayer和 options 實例,而不要在執行緒之間共享同一個實例。轉換會改變座標系統嗎? 預設情況下不會。若要在轉換過程中重新投影,請在呼叫
Convert之前將ConversionOptions.DestinationSpatialReferenceSystem設定為目標系統。
