TopoJSON — это компактное кодирование географических данных, и обычно каждый объект в файле TopoJSON содержит объект атрибутов, а не плоский набор пар ключ‑значение. Экспорт этой структуры в CSV — место, где обычно возникают проблемы: большинство инструментов либо отбрасывают вложенный объект, либо выводят его обратно в виде строки JSON, помещённой в одну ячейку. В этом руководстве показано как преобразовать TopoJSON в CSV на C# с помощью Aspose.GIS, сохраняя каждый атрибут доступным в отдельном столбце и при желании записывая геометрию рядом в виде Well‑Known Text.

Преимущество заключается в том, что иерархический набор данных GIS преобразуется в плоскую таблицу, которую SQL Server, Power BI, pandas или обычная электронная таблица могут использовать напрямую, без необходимости создавать собственный слой парсинга JSON в приложении.

Почему стоит конвертировать TopoJSON в CSV с помощью Aspose.GIS

Рассмотрите объект 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. Снова откройте CSV с помощью Drivers.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) считывает типизированное значение атрибута. Используйте GetValueOrDefault<T>(string), когда столбец может отсутствовать или быть null, поскольку GetValue<T> предполагает, что значение присутствует.
  • Последняя проверка — та, что удивляет людей. В CSV‑файле нет столбца геометрии, если вы его не создадите, поэтому каждая запись возвращается с пустой геометрией. Это поведение по умолчанию, а не ошибка конвертации, и следующая секция исправляет это.

Оборачивание обоих слоёв в блоки using имеет значение здесь: VectorLayer реализует IDisposable, и файл назначения не гарантировано будет сброшен и закрыт, пока он не будет уничтожен.

Включить геометрию в виде WKT с помощью CsvOptions

Что вы достигнете: Создайте CSV, который содержит как уплощённые атрибуты и столбец, содержащий геометрию каждого объекта в виде Well-Known Text.

Пошаговый процесс

  1. Создайте экземпляр CsvOptions и установите ColumnWkt в выбранное вами имя столбца.
  2. Присвойте его ConversionOptions.DestinationDriverOptions.
  3. Вызовите перегрузку VectorLayer.Convert, принимающую параметры конвертации.
  4. Откройте CSV заново с теми же CsvOptions, чтобы драйвер знал, какой столбец парсить как геометрию.
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 одинаковый как для одного файла, так и для тысячи. Единственное правило, которое стоит помнить при параллелизации, — каждый рабочий процесс нуждается в своих собственных объектах 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");

Бесплатные дополнительные ресурсы

Заключение

Преобразование TopoJSON с вложенными свойствами в CSV в .NET сводится к одной полезной особенности: Aspose.GIS преобразует вложенные объекты атрибутов в именованные столбцы, такие как prop1_this, во время чтения слоя, поэтому VectorLayer.Convert не требует пользовательской логики для их сохранения. Добавьте CsvOptions.ColumnWkt, когда downstream system нуждается в геометрии, добавьте DestinationSpatialReferenceSystem, когда требуется конкретная проекция, и повторяйте тот же вызов, когда нужно обработать папку. Результатом является плоская, готовая к анализу таблица, созданная несколькими строками C#, без необходимости поддерживать собственный слой парсинга JSON.

FAQs

  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 не содержит столбца геометрии, если вы явно не запросите его. Установите ColumnWkt в CsvOptions, которые вы передаёте как при записи, так и при чтении, иначе драйвер не будет иметь столбца, из которого можно построить геометрию.

  5. Какие исключения следует обрабатывать при конвертации?
    Документированные исключения: ArgumentNullException для нулевого пути, ArgumentException, когда объект параметров не соответствует драйверу, GisException, когда невозможно прочитать объект из файла, и IOException для ошибок ввода‑вывода на уровне системы.

  6. Могу ли я конвертировать пакет файлов TopoJSON за один раз? Да. Пройдите по списку файлов и вызовите VectorLayer.Convert для каждого файла. Если вы параллелизуете цикл, предоставьте каждому рабочему своему собственному экземпляру VectorLayer и параметров, а не делитесь одним экземпляром между потоками.

  7. Изменяет ли преобразование систему координат? По умолчанию нет. Чтобы выполнить репроекцию во время преобразования, установите ConversionOptions.DestinationSpatialReferenceSystem в целевую систему перед вызовом Convert.

Читать дальше