Skip to content

Round-trip Chord, Phrase, Command and Profile through System.Text.Json [patch] - #178

Open
matt-edmondson wants to merge 1 commit into
mainfrom
fix/141-json-round-trip
Open

matt-edmondson wants to merge 1 commit into
mainfrom
fix/141-json-round-trip

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #141

What changed

  • Chord / Phrase: each gets a private [JsonConstructor] taking IReadOnlyList<Note> / IReadOnlyList<Chord>. System.Text.Json binds constructor parameters by name and exact type, so marking the existing IEnumerable<T> constructors (as the issue suggested) fails with InvalidOperationException. The new constructors just forward to the public ones, so deduplication and validation still apply.
  • Command: a new internal SemanticStringJsonConverter<T> is applied to Id, Name, Description and Category, so they serialize as JSON strings and not as char arrays. The semantic-string constructor is marked [JsonConstructor].
  • Profile: [JsonObjectCreationHandling(Populate)] isn't supported on a type with a parameterized constructor, so a private [JsonConstructor] takes a chords dictionary and binds each entry through SetChord. This keeps the existing trimming and null checks.

The private constructors carry an IDE0051 suppression, because only the serializer calls them.

Tests

ModelJsonSerializationTests (6 tests) sits next to the existing NoteJsonSerializationTests. It covers round-trips for Chord, Phrase, Command (with and without description/category) and Profile (including its bound chords), and checks that the Command fields serialize as JSON strings.

  • With the fix reverted, all 6 fail with the errors the issue describes.
  • With the fix, all 6 pass. The full suite passes (203/203) and Keybinding.sln builds clean on net8.0, net9.0 and net10.0.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T4yQx7YuowTGorXJRkhe8x


Generated by Claude Code

…n [patch]

Chord and Phrase had two public constructors and no [JsonConstructor], so
deserializing their own output threw NotSupportedException. Each now has a
private [JsonConstructor] taking its property's own type, which is what the
serializer binds against.

Command's semantic strings serialized as char arrays. A
SemanticStringJsonConverter writes them as plain strings and reads them back
through Create, and the semantic-string constructor is marked
[JsonConstructor].

Profile.Chords is get-only, so a deserialized profile silently came back with
no bindings. A private [JsonConstructor] takes the chords and binds them
through SetChord. Populate handling was not an option: System.Text.Json does
not support it on a type with a parameterized constructor.

Fixes #141

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4yQx7YuowTGorXJRkhe8x
@sonarqubecloud

sonarqubecloud Bot commented Oct 8, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant