Skip to content

JsonSerializationProvider.TrySerialize and Deserialize throw InvalidOperationException instead of returning false/default #161

Closed

Description

@matt-edmondson

What's wrong

JsonSerializationProvider is the default serializer. Its docs promise that TrySerialize returns "True if the serialization was successful, false otherwise" and that Deserialize<T> returns "default if deserialization fails". The methods only catch JsonException, NotSupportedException, and IOException/ObjectDisposedException (serialize) or ArgumentException (deserialize).

System.Text.Json reports two common type-shape errors as InvalidOperationException, and both escape these methods:

  • two members that map to the same JSON name
  • a deserialization constructor whose parameters do not bind to properties

The affected code is in Essentials.SerializationProviders.Json/JsonSerializationProvider.cs:

  • TrySerialize (L55-L84)
  • Deserialize<T> (L92-L115)

The YAML provider already treats this case as a failure (YamlSerializationProvider.cs L91, catch (InvalidOperationException) { return default; }). The Newtonsoft provider reports these errors as JsonSerializationException, which it catches. That makes the default JSON provider the only one that breaks its Try/Deserialize contract here.

Reproduction (verified on net10.0 against the project)

ISerializationProvider p = new JsonSerializationProvider();
p.TrySerialize(new Dup { A = 1, B = 2 }, new StringWriter());
// throws InvalidOperationException: The JSON property name for 'Dup.x' collides with another property.
p.Deserialize<Dup>("{\"x\":1}"u8);      // throws the same
p.Deserialize<BadCtor>("{\"Value\":1}"u8);
// throws InvalidOperationException: Each parameter in the deserialization constructor on type 'BadCtor' must bind to an object property or field...

public class Dup { [JsonPropertyName("x")] public int A { get; set; } [JsonPropertyName("x")] public int B { get; set; } }
public class BadCtor { [JsonConstructor] public BadCtor(int other) { } public int Value { get; set; } }

The second case needs no attributes. A class whose only constructor takes a parameter not named after a property hits it, for example class C(string p) { public string Path { get; } = p; }.

Suggested fix / acceptance criteria

Activity

  1. matt-edmondson commented on Oct 20, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions