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 文件,并读取结果以确认列已成功传递。

逐步过程

  1. 定义源 TopoJSON 路径和目标 CSV 路径。
  2. 使用 VectorLayer.Open 打开源并确认已存在展平的属性。
  3. 调用带有源和目标驱动的静态 VectorLayer.Convert 重载。
  4. 使用 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 列。

逐步过程

  1. 创建一个 CsvOptions 实例,并将 ColumnWkt 设置为您选择的列名。
  2. 将其分配给 ConversionOptions.DestinationDriverOptions。
  3. 调用接受转换选项的 VectorLayer.Convert 重载。
  4. 使用相同的 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 解析层。

常见问题

  1. Can Aspose.GIS flatten nested JSON objects when converting to CSV? 是的。TopoJSON 驱动程序在打开图层时会将嵌套的属性对象显示为单独的列,并使用下划线连接父子名称,因此在转换运行之前已经完成了展平。

  2. 我需要编写自定义代码来处理嵌套属性吗? 不。VectorLayer.Convert 会写入源图层公开的所有列,因此嵌套属性不需要您额外的解析代码。

  3. 如何在 CSV 输出中包含几何信息? 创建一个 CsvOptions 实例,将其 ColumnWkt 属性设置为您想要的列名,将其分配给 ConversionOptions.DestinationDriverOptions,然后将其传递给 VectorLayer.Convert。

  4. 读取 CSV 时为什么几何信息为空? CSV 除非你指定,否则没有几何列。请在写入和读取时传递的 CsvOptions 上设置 ColumnWkt,否则驱动程序将没有列来构建几何对象。

  5. 在转换过程中应该处理哪些异常? 文档中列出的异常包括 ArgumentNullException(当路径为 null 时),ArgumentException(当选项对象与驱动程序不匹配时),GisException(当无法从文件读取特征时),以及 IOException(底层 I/O 失败)。

  6. 我可以一次性转换一批 TopoJSON 文件吗? 是的。遍历文件列表,对每个文件调用 VectorLayer.Convert。如果并行化循环,请为每个工作线程提供各自的 VectorLayer 和 options 实例,而不是在多个线程之间共享同一个实例。

  7. 转换是否会更改坐标系? 默认情况下不会。若要在转换期间重新投影,请在调用 Convert 之前将 ConversionOptions.DestinationSpatialReferenceSystem 设置为目标系统。

阅读更多