Skip to content

Chord, Phrase, Command and Profile don't round-trip through System.Text.Json: deserialize throws NotSupportedException, Command ids serialize as char arrays, Profile silently loses its chords #141

Description

@matt-edmondson

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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreadyFully specified; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions