Skip to main content

Goal

Export structured model data from a Speckle version to a CSV file you can open in Excel — door schedules, room lists, or custom property reports.

What you will build

A script that loads a model, finds doors (or rooms), extracts name, level, type, and fire rating from DataObject.properties, and writes door-schedule.csv.

When to use this

Use this when a BIM manager, architect, or engineer needs a spreadsheet report from published model data — schedules, QA lists, or quantity exports. This extends the pattern from Build your first model analysis tool.
  1. Load the latest version with Receive2 (see Automate with scripts)
  2. Flatten() the object graph once
  3. Filter DataObject instances by properties["category"] or similar
  4. Read BIM fields from properties — not from typed C# classes
  5. Write CSV with StreamWriter, escaping commas in cell values
Avoid this unless you need Excel-specific formatting — for most reports, CSV is the simplest path.

Complete example

https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251Complete example
using Speckle.Objects.Data;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Extensions;

static string? GetProp(DataObject obj, params string[] keys)
{
    foreach (var key in keys)
        if (obj.properties.TryGetValue(key, out var val) && val is string s)
            return s;
    return null;
}

static bool IsDoor(Base obj) =>
    obj is DataObject d &&
    (GetProp(d, "category", "builtInCategory")?.Contains("Door", StringComparison.OrdinalIgnoreCase) ?? false);

var doors = root.Flatten().Where(IsDoor).Cast<DataObject>().ToList();

var outputPath = "door-schedule.csv";
await using (var writer = new StreamWriter(outputPath))
{
    await writer.WriteLineAsync("Name,Level,Type,FireRating");
    foreach (var door in doors)
    {
        var name = door.name ?? "";
        var level = GetProp(door, "level", "Level") ?? "";
        var type = GetProp(door, "type", "family", "Family") ?? "";
        var fireRating = GetProp(door, "fireRating", "Fire Rating") ?? "";

        static string CsvCell(string value) =>
            value.Contains(',') || value.Contains('"') ? $"\"{value.Replace("\"", "\"\"")}\"" : value;

        await writer.WriteLineAsync(
            $"{CsvCell(name)},{CsvCell(level)},{CsvCell(type)},{CsvCell(fireRating)}");
    }
}

Console.WriteLine($"Exported {doors.Count} doors to {outputPath}");

Room schedule variant

Filter on category containing Room and export name, area, and level:
https://mintcdn.com/speckle/VtRPWzmN-ULoLfIv/images/developers/sdks/csharp.svg?fit=max&auto=format&n=VtRPWzmN-ULoLfIv&q=85&s=f2970e3b8bd4a2c7899b46211a42f251Example
var rooms = root.Flatten()
    .OfType<DataObject>()
    .Where(d => GetProp(d, "category")?.Contains("Room", StringComparison.OrdinalIgnoreCase) == true);

foreach (var room in rooms)
{
    var area = room.properties.GetValueOrDefault("area")?.ToString() ?? "";
    // write name, area, level to CSV
}
Property names vary by connector and host app. Inspect a sample object in the Speckle viewer or use Find objects by property to discover the keys your model uses.

How it works

Connector-published BIM data is stored as DataObject with semantics in properties. There are no typed Door or Room classes in Speckle Object Model v3 — you filter and read dictionaries. Flatten() gives you every object in one pass. Build your CSV from that list rather than walking the hierarchy manually unless you need parent context.

Common mistakes

MistakeFix
Assuming property names match Revit UI labels exactlyLog properties.Keys from one sample object first
Checking speckle_type for “Wall” or “Door”Filter on properties["category"] instead
Not escaping commas in CSV cellsWrap values in quotes when they contain commas
Calling Flatten() per rowFlatten once, then filter

Next steps

Find objects by property

Filter, index, and QA checks

BIM data patterns

How connector data is structured

Send analysis results back

Publish QA results to Speckle
Last modified on July 10, 2026