What's wrong
#122 made Note round-trip through System.Text.Json (NoteJsonConverter, [JsonConverter] on Note). The public models built on it still don't round-trip with the default JsonSerializer:
| Type |
Serialize |
Deserialize its own output |
Chord (Models/MusicalTypes.cs ~119/135) |
{"Notes":[{"Key":"CTRL"},{"Key":"S"}]} (fine) |
throws NotSupportedException: two public constructors, no [JsonConstructor] |
Phrase (Models/MusicalTypes.cs ~320/335) |
fine |
throws NotSupportedException, same reason |
Command (Models/Command.cs ~67-82) |
{"Id":["f","i","l","e",".","s","a","v","e"],"Name":["S","a","v","e"],...}: the SemanticString CommandId / CommandName / CommandDescription / CommandCategory serialize as char arrays, the same defect #122 fixed for NoteName |
throws NotSupportedException: no usable constructor |
Profile (Models/Profile.cs ~73) |
"Chords":{"file.save":{...}} |
succeeds but Chords.Count == 0: Chords is a get-only Dictionary and nothing tells STJ to populate it, so the bindings are dropped without any error |
These were reproduced on HEAD (9e3c81f) with a small net10.0 console app that references Keybinding.csproj, serializes each type and then deserializes the result.
Why it matters
JsonKeybindingRepository goes through private DTOs, so the library's own persistence is unaffected. But Note already carries a converter so the models serialize, and anyone storing, caching or returning these public types themselves hits the problems above. A web or IPC endpoint that returns a Command sends char arrays. A settings store that persists a Profile comes back with no bindings, and it does so silently.
Suggested fix
- Serialize the Command SemanticString types as plain strings, either with a string converter per type or with one converter on
Command, and give Command a [JsonConstructor].
- Mark
Chord(IEnumerable<Note> notes) and Phrase(IEnumerable<Chord> sequence) with [JsonConstructor]. Their parameter names already match the Notes and Sequence properties.
- For
Profile.Chords, add [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)], or bind it through a constructor parameter.
Acceptance criteria
JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(x)) equals x for Command, Chord, Phrase and Profile, the last including its bound chords.
- The command id, name, description and category serialize as JSON strings.
- Tests are added alongside the existing
Note JSON serialization tests.
What's wrong
#122 made
Noteround-trip through System.Text.Json (NoteJsonConverter,[JsonConverter]onNote). The public models built on it still don't round-trip with the defaultJsonSerializer:Chord(Models/MusicalTypes.cs~119/135){"Notes":[{"Key":"CTRL"},{"Key":"S"}]}(fine)NotSupportedException: two public constructors, no[JsonConstructor]Phrase(Models/MusicalTypes.cs~320/335)NotSupportedException, same reasonCommand(Models/Command.cs~67-82){"Id":["f","i","l","e",".","s","a","v","e"],"Name":["S","a","v","e"],...}: the SemanticStringCommandId/CommandName/CommandDescription/CommandCategoryserialize as char arrays, the same defect #122 fixed forNoteNameNotSupportedException: no usable constructorProfile(Models/Profile.cs~73)"Chords":{"file.save":{...}}Chords.Count == 0:Chordsis a get-onlyDictionaryand nothing tells STJ to populate it, so the bindings are dropped without any errorThese were reproduced on HEAD (9e3c81f) with a small net10.0 console app that references
Keybinding.csproj, serializes each type and then deserializes the result.Why it matters
JsonKeybindingRepositorygoes through private DTOs, so the library's own persistence is unaffected. ButNotealready carries a converter so the models serialize, and anyone storing, caching or returning these public types themselves hits the problems above. A web or IPC endpoint that returns aCommandsends char arrays. A settings store that persists aProfilecomes back with no bindings, and it does so silently.Suggested fix
Command, and giveCommanda[JsonConstructor].Chord(IEnumerable<Note> notes)andPhrase(IEnumerable<Chord> sequence)with[JsonConstructor]. Their parameter names already match theNotesandSequenceproperties.Profile.Chords, add[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)], or bind it through a constructor parameter.Acceptance criteria
JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(x))equalsxforCommand,Chord,PhraseandProfile, the last including its bound chords.NoteJSON serialization tests.