How to programmatically choose a constructor during deserialization?

A ClaimConverter has been packaged with IdentityServer4.

Namespace: IdentityServer4.Stores.Serialization

Example of use:

JsonConvert.DeserializeObject<T>(value, new IdentityServer4.Stores.Serialization.ClaimConverter());

If it is not possible to add a [JsonConstructor] attribute to the target class (because you don't own the code), then the usual workaround is to create a custom JsonConverter as was suggested by @James Thorpe in the comments. It is pretty straightforward. You can load the JSON into a JObject, then pick the individual properties out of it to instantiate your Claim instance. Here is the code you would need:

class ClaimConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(System.Security.Claims.Claim));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        string type = (string)jo["Type"];
        string value = (string)jo["Value"];
        string valueType = (string)jo["ValueType"];
        string issuer = (string)jo["Issuer"];
        string originalIssuer = (string)jo["OriginalIssuer"];
        return new Claim(type, value, valueType, issuer, originalIssuer);
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To use the converter, simply pass an instance of it to the JsonConvert.DeserializeObject<T>() method call:

Claim claim = JsonConvert.DeserializeObject<Claim>(json, new ClaimConverter());

Fiddle: https://dotnetfiddle.net/7LjgGR


Another approach, which will work for non-sealed classes at least, is to subclass it, but with only the constructor you're interested in:

class MyClaim : Claim {
    public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer):
        base(type, value, valueType, issuer, originalIssuer){}
}

You can then deserialize to this object with no helper classes, and then treat it as the base type.

Claim claim = JsonConvert.DeserializeObject<MyClaim>(json);

For sealed classes, you could take this approach (pretending for a second that Claim is sealed):

class MyClaim {
    private Claim _claim;
    public MyClaim(string type, string value, string valueType, string issuer, string originalIssuer) {
        _claim = new Claim(type, value, valueType, issuer, originalIssuer);
    }
    public Claim Value { get {
            return _claim;
        }
    }
}

Claim claim = JsonConvert.DeserializeObject<MyClaim>(json).Value;

Tags:

C#

Json.Net