Skip to content

v1.1.0 — Spec compliance, IEnumerable<T> support, source generator fixes#14

Merged
iamadamreed merged 14 commits intomainfrom
alpha
Mar 8, 2026
Merged

v1.1.0 — Spec compliance, IEnumerable<T> support, source generator fixes#14
iamadamreed merged 14 commits intomainfrom
alpha

Conversation

@iamadamreed
Copy link
Owner

Summary

  • Spec compliance — nested objects now use block format; block-format array headers include column names; deserializer correctly handles block-format arrays with column headers (resolves Clarify: When to use tabular vs block format for objects with collection properties tonl-dev/tonl#27)
  • IEnumerable<T> support — root-level enumerable collections serialize correctly
  • Source generator fixes — duplicate hintName/class names, context-registered types, non-instantiable types, recursive type discovery
  • Bug fixesSerializeToString missing Flush(), root-level collection serialization, nested collection properties in tabular rows

Test plan

  • dotnet build — clean (0 errors)
  • dotnet test — 360/360 passing on net9.0 and net10.0
  • New spec compliance tests: Complex Nested Example, tabular-vs-block decision, constraints example (issue #27)

Use SafePropertyName instead of TypeName for generated file names.
This prevents collisions when registering multiple generic types with
the same base type (e.g., List<TypeA> and List<TypeB>).
- Fix duplicate local variable names in deserialization by adding unique
  index suffixes to pattern match variables (n0, s0, v0, etc.)
- Generate serializers for all concrete context types, not just those
  with parameterless constructors (needed for PropertyNames, WriteRow)
- Skip Deserialize method generation for non-instantiable types
- Add writer.Flush() before returning string in generated SerializeToString
- Add GeneratePropertyWriteForTopLevel for AOT-compatible direct serialization
- Add recursive type discovery for nested object properties and collection elements
Auto-discover element types from generic type arguments (e.g., List<T> → T)
when types are registered on a context. This matches System.Text.Json behavior.

- Add DiscoverTypeArguments helper method
- Call from DiscoverReferencedTypes to discover types from generic arguments
- Add tests for type discovery and de-duplication
- Suppress nullable warnings in generated code (pre-existing generator issue)
When collection types (Dictionary, List, Array) are registered directly
on a context, serialize their contents instead of CLR properties like
Capacity, Count, Comparer, Keys, Values.

Changes:
- Add collection metadata fields to SerializableTypeInfo
- Detect collections in ExtractTypeInfoFromSymbol and set empty Properties
- Add collection-specific serialization code generation
- Add array type support in attribute processing
- Update TonlSerializer header handling for collection types
- Add TonlTypeInfo collection metadata (IsCollection, IsDictionary, etc.)
- Add comprehensive test coverage for root-level collections
Tests verify that IEnumerable<string> and IEnumerable<TableInfo> serialize
their elements properly instead of CLR interface metadata.
…ializers

GeneratePropertyWriteForTopLevel and GeneratePropertyWriteForNested now handle
PropertyCategory.Collection and PropertyCategory.Object instead of falling
through to default which called .ToString().

Adds 4 tests for nested collection serialization scenarios.
GenerateRowValueWrite now handles PropertyCategory.Collection and
PropertyCategory.Object instead of falling through to default which
called .ToString().

- Collections of primitives: joined with semicolon delimiter
- Collections of objects: serialized inline using WriteRowInline
- Dictionaries: serialized as key=value pairs
- Nested objects: use WriteRowInline

Adds 7 tests for WriteRow collection serialization scenarios.
Per TONL spec, objects containing collection or nested object properties
should use block format (WriteProperties) instead of tabular format
(WriteRow). This prevents incorrectly inlining collections with semicolons.

- Add HasOnlyPrimitiveProperties flag to SerializableTypeInfo
- Add ElementHasOnlyPrimitives/ObjectHasOnlyPrimitives to PropertyInfo
- Add TypeHasOnlyPrimitiveProperties() helper to analyze type symbols
- Update GenerateCollectionWriteForTopLevel() to select format based on flag
- Update GenerateArrayWrite() and GenerateRootListOfObjectsWrite() similarly

Simple objects (only primitives) continue using tabular format.
Complex objects (has collections/objects) now use block format.
…rs, deserializer block detection

Closes #10, #11, #12

Serializer (CodeGenerator.cs):
- Nested objects now emit block format (key-value per line) instead of inline
  format. Both GenerateNestedObjectWriteForTopLevel and GenerateNestedObjectWrite
  now use WriteNewLine + WriteProperties instead of WriteByte(' ') + WriteRowInline.
- Block-format arrays now include column headers. WritePrimitiveArrayHeader
  replaced with WriteArrayHeader+PropertyNames in GenerateCollectionWriteForTopLevel
  and GenerateArrayWrite so complex-element arrays emit key[N]{col1,col2,...}:.

Deserializer (TonlSerializer.cs):
- DeserializeDocument can now detect and parse block-format arrays that carry
  column headers (key[N]{cols}:). Peeks at the next content line to determine
  tabular vs block mode. Block mode tracks item boundaries by the first column
  name repeating at item-indent depth. Handles nested tabular arrays, nested
  objects, and inline primitive arrays within block-format items.
…sting, tabular-vs-block

Closes #13

- Rename SPEC_002 test from NestedObjectInline to NestedObject_ProducesBlockFormat
  and update assertion to verify block-format output (separate lines for each field)
- Add ComplexNestedExample_SerializesCorrectFormat: verifies the spec's flagship
  example (Project/Owner/Tasks/Assignee/Comments hierarchy) produces correct
  block format with tabular Comments nested inside block Tasks
- Add ComplexNestedExample_RoundTrips: serialize and deserialize the complex nested
  structure, assert key values are preserved
- Add ArrayOfAllPrimitiveObjects_UsesTabularFormat: decision test for tabular path
- Add ArrayOfObjectsWithCollectionProperty_UsesBlockFormat: decision test for block
  path (type with List<string> property must use block format)
- Add ConstraintList_BlockFormat_IncludesColumnHeader: issue #27 constraints example
@iamadamreed iamadamreed merged commit 1f7a8c1 into main Mar 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clarify: When to use tabular vs block format for objects with collection properties

1 participant