TopoJSON 是一种紧凑的地理数据编码,通常在 TopoJSON 文件中的每个要素都会携带一个属性对象,而不是一组平面的键值对。将该结构导出为 CSV 时,往往会出现问题:大多数工具要么丢弃嵌套对象,要么把它作为 JSON 字符串塞进单元格。本文指南展示了 如何使用 Aspose.GIS 在 C# 中将 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,其中既包含展平的属性 以及 一个保存每个要素几何形状的 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(...),如果不受支持,则回退到空选项对象。
批量转换文件夹中的 TopoJSON 文件
API 对单个文件和成千上万个文件来说是相同的。并行处理时唯一需要记住的规则是,每个工作线程都需要拥有自己的 layer 和 options 对象。
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 解析层。
常见问题
Can Aspose.GIS flatten nested JSON objects when converting to CSV? 是的。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设置为目标系统。
