Related
The JSON response from my ASP.NET Core 3.1 API controller is missing properties. This happens when a property uses a derived type; any properties defined in the derived type but not in the base/interface will not be serialized to JSON. It seems there is some lack of support for polymorphism in the response, as if serialization is based on a property's defined type instead of its runtime type. How can I change this behavior to ensure that all public properties are included in the JSON response?
Example:
My .NET Core Web API Controller returns this object that has a property with an interface type.
// controller returns this object
public class Result
{
public IResultProperty ResultProperty { get; set; } // property uses an interface type
}
public interface IResultProperty
{ }
Here is a derived type that defines a new public property named Value.
public class StringResultProperty : IResultProperty
{
public string Value { get; set; }
}
If I return the derived type from my controller like this:
return new MainResult {
ResultProperty = new StringResultProperty { Value = "Hi there!" }
};
then the actual response includes an empty object (the Value property is missing):
I want the response to be:
{
"ResultProperty": { "Value": "Hi there!" }
}
While the other answers are good and solves the problem, if all you want is the general behavior to be like pre netcore3, you can use the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package and in Startup.cs do:
services.AddControllers().AddNewtonsoftJson()
More info here. This way, you don't need to create any extra json-converters.
I ended up creating a custom JsonConverter (System.Text.Json.Serialization namespace) which forces JsonSerializer to serialize to the object's runtime type. See the Solution section below. It's lengthy but it works well and does not require me to sacrifice object oriented principles in my API's design. (If you need something quicker and can use Newtonsoft then check out the top voted answer instead.)
Some background: Microsoft has a System.Text.Json serialization guide with a section titled Serialize properties of derived classes with good information relevant to my question. In particular it explains why properties of derived types are not serialized:
This behavior is intended to help prevent accidental exposure of data
in a derived runtime-created type.
If that is not a concern for you then the behavior can be overridden in the call to JsonSerializer.Serialize by either explicitly specifying the derived type or by specifying object, for example:
// by specifying the derived type
jsonString = JsonSerializer.Serialize(objToSerialize, objToSerialize.GetType(), serializeOptions);
// or specifying 'object' works too
jsonString = JsonSerializer.Serialize<object>(objToSerialize, serializeOptions);
To accomplish this with ASP.NET Core you need to hook into the serialization process. I did this with a custom JsonConverter that calls JsonSerializer.Serialize one of the ways shown above. I also implemented support for deserialization which, while not explicitly asked for in the original question, is almost always needed anyway. (Oddly, supporting only serialization and not deserialization proved to be tricky anyway.)
Solution
I created a base class, DerivedTypeJsonConverter, which contains all of the serialization & deserialization logic. For each of your base types, you would create a corresponding converter class for it that derives from DerivedTypeJsonConverter. This is explained in the numbered directions below.
This solution follows the "type name handling" convention from Json.NET which introduces support for polymorphism to JSON. It works by including an additional $type property in the derived type's JSON (ex: "$type":"StringResultProperty") that tells the converter what the object's true type is. (One difference: in Json.NET, $type's value is a fully qualified type + assembly name, whereas my $type is a custom string which helps future-proof against namespace/assembly/class name changes.) API callers are expected to include $type properties in their JSON requests for derived types. The serialization logic solves my original problem by ensuring that all of the object's public properties are serialized, and for consistency the $type property is also serialized.
Directions:
1) Copy the DerivedTypeJsonConverter class below into your project.
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
public abstract class DerivedTypeJsonConverter<TBase> : JsonConverter<TBase>
{
protected abstract string TypeToName(Type type);
protected abstract Type NameToType(string typeName);
private const string TypePropertyName = "$type";
public override bool CanConvert(Type objectType)
{
return typeof(TBase) == objectType;
}
public override TBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// get the $type value by parsing the JSON string into a JsonDocument
JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader);
jsonDocument.RootElement.TryGetProperty(TypePropertyName, out JsonElement typeNameElement);
string typeName = (typeNameElement.ValueKind == JsonValueKind.String) ? typeNameElement.GetString() : null;
if (string.IsNullOrWhiteSpace(typeName)) throw new InvalidOperationException($"Missing or invalid value for {TypePropertyName} (base type {typeof(TBase).FullName}).");
// get the JSON text that was read by the JsonDocument
string json;
using (var stream = new MemoryStream())
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Encoder = options.Encoder })) {
jsonDocument.WriteTo(writer);
writer.Flush();
json = Encoding.UTF8.GetString(stream.ToArray());
}
// deserialize the JSON to the type specified by $type
try {
return (TBase)JsonSerializer.Deserialize(json, NameToType(typeName), options);
}
catch (Exception ex) {
throw new InvalidOperationException("Invalid JSON in request.", ex);
}
}
public override void Write(Utf8JsonWriter writer, TBase value, JsonSerializerOptions options)
{
// create an ExpandoObject from the value to serialize so we can dynamically add a $type property to it
ExpandoObject expando = ToExpandoObject(value);
expando.TryAdd(TypePropertyName, TypeToName(value.GetType()));
// serialize the expando
JsonSerializer.Serialize(writer, expando, options);
}
private static ExpandoObject ToExpandoObject(object obj)
{
var expando = new ExpandoObject();
if (obj != null) {
// copy all public properties
foreach (PropertyInfo property in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead)) {
expando.TryAdd(property.Name, property.GetValue(obj));
}
}
return expando;
}
}
2) For each of your base types, create a class that derives from DerivedTypeJsonConverter. Implement the 2 abstract methods which are for mapping $type strings to actual types. Here is an example for my IResultProperty interface that you can follow.
public class ResultPropertyJsonConverter : DerivedTypeJsonConverter<IResultProperty>
{
protected override Type NameToType(string typeName)
{
return typeName switch
{
// map string values to types
nameof(StringResultProperty) => typeof(StringResultProperty)
// TODO: Create a case for each derived type
};
}
protected override string TypeToName(Type type)
{
// map types to string values
if (type == typeof(StringResultProperty)) return nameof(StringResultProperty);
// TODO: Create a condition for each derived type
}
}
3) Register the converters in Startup.cs.
services.AddControllers()
.AddJsonOptions(options => {
options.JsonSerializerOptions.Converters.Add(new ResultPropertyJsonConverter());
// TODO: Add each converter
});
4) In requests to the API, objects of derived types will need to include a $type property. Example JSON: { "Value":"Hi!", "$type":"StringResultProperty" }
Full gist here
The documentation shows how to serialize as the derived class when calling the serializer directly. The same technique can also be used in a custom converter that we then can tag our classes with.
First, create a custom converter
public class AsRuntimeTypeConverter<T> : JsonConverter<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<T>(ref reader, options);
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, value?.GetType() ?? typeof(object), options);
}
}
Then mark the relevant classes to be used with the new converter
[JsonConverter(typeof(AsRuntimeTypeConverter<MyBaseClass>))]
public class MyBaseClass
{
...
Alternately, the converter can be registered in startup.cs instead
services
.AddControllers(options =>
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new AsRuntimeTypeConverter<MyBaseClass>());
}));
I had a similar issue, where I was returning an enumerable of type TAnimal (but the object instances were of derived types such as Dog, Cat, etc.):
[HttpGet]
public IEnumerable<TAnimal> GetAnimals()
{
IEnumerable<TAnimal> list = GetListOfAnimals();
return list;
}
This only included properties defined in TAnimal.
However, in ASP .NET Core 3.1 at least, I found that I could just cast the object instances to object, and the JSON serializer then included all the properties from the derived classes:
[HttpGet]
public IEnumerable<object> GetAnimals()
{
IEnumerable<TAnimal> list = GetListOfAnimals();
return list.Select(a => (object)a);
}
(Note that the signature of the GetAnimals method must also changed, but that doesn't usually matter much in a web API context). If you need to provide type information for Swagger or whatever, you can annotate the method:
[HttpGet]
[Produces(MediaTypeNames.Application.Json, Type = typeof(TAnimal[]))]
public IEnumerable<object> GetAnimals()
{
...
}
Casting to object is a simple solution if you only have a 1-layer-deep object hierarchy to worry about.
This is the expected result. You're upcasting when you do that, so what will be serialized is the upcasted object, not the actual derived type. If you need stuff from the derived type, then that has to be the type of the property. You may want to use generics for this reason. In other words:
public class Result<TResultProperty>
where TResultProperty : IResultProperty
{
public TResultProperty ResultProperty { get; set; } // property uses an interface type
}
Then:
return new Result<StringResultProperty> {
ResultProperty = new StringResultProperty { Value = "Hi there!" }
};
I solved it by writing this extension:
public static class JsonSerializationExtensions
{
public static string ToJson<T>(this IEnumerable<T> enumerable, bool includeDerivedTypesProperties = true)
where T : class
{
var jsonOptions = new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
if (includeDerivedTypeProperties)
{
var collection = enumerable.Select(e => e as object).ToList();
return JsonSerializer.Serialize<object>(collection, jsonOptions);
}
else
{
return JsonSerializer.Serialize(enumerable, jsonOptions);
}
}
}
I was also struggling with this in a .NET Core 3.1 API, where I wanted the result to include $type attribute.
As suggested, install the correct package and then 'AddNewtonsoftJson'.
I wanted the $type field to be added to show the derived type handling, to get that
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All;
});
Not knocking Newtonsoft, but I found an easier way to resolve this with the built handlers.
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/emps", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
List<emp> GetEmps();
//[DataContract(Namespace = "foo")] <<< comment/removed this line
public class emp
{
public string userId { get; set; }
public string firstName { get; set; }
}
public class dept
{
public string deptId{ get; set; }
public string deptName{ get; set; }
}
In my case dept objects where working fine, but emp ones were not - they came across as empty.
I would like to deserialize a System.Security.Claims.Claim object serialized in the following way:
{
"Issuer" : "LOCAL AUTHORITY",
"OriginalIssuer" : "LOCAL AUTHORITY",
"Type" : "http://my.org/ws/2015/01/identity/claims/mytype",
"Value" : "myvalue",
"ValueType" : "http://www.w3.org/2001/XMLSchema#string"
}
What I get is a JsonSerializationException:
Unable to find a constructor to use for type
System.Security.Claims.Claim. A class should either have a default
constructor, one constructor with arguments or a constructor marked
with the JsonConstructor attribute.
After some investigation I finally understand the meaning of one in the above message: The JSON deserializer cannot find the right constructor as there are - in the case of the Claim type - multiple constructors with arguments (although there exists a constructor with arguments matching exactly the above properties).
Is there a way to tell the deserializer which constructor to choose without adding the JsonConstructor attribute to that mscorlib type?
Daniel Halan has solved this issue with a patch to Json.NET a few years ago. Is there a way to solve this without modifying Json.NET these days?
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;
A ClaimConverter has been packaged with IdentityServer4.
Namespace: IdentityServer4.Stores.Serialization
Example of use:
JsonConvert.DeserializeObject<T>(value, new IdentityServer4.Stores.Serialization.ClaimConverter());
Thanks #chen-zhe, this was the simplest solution among above posts including accepted answer.
In my case, I needed to Deserializing entire Clients (Is4 model) including Claims which was just one of the child class of Client.
Here is what I tried and worked out well -
[HttpPost]
public async Task<ActionResult<Client>> Post(Object model)
{
var clientString = model.ToString();
Client client = JsonConvert.DeserializeObject<Client>(clientString, new ClaimConverter());
}
Things to note to make this work:
First you might want to do away with default binding of framework in my case (ASP.NET Core [FromBody] attribute and
Resolve ClaimCoverter to native Id4 framework library class in IdentityServer4.Stores.Serialization and no need to reinvent the wheel creating your custom JsonConverter suggested above or even .NET Core Custom Model binding approach
Extending James Thorpe answer we are using DeserializableClaim class to read from appsetting configuration
/// <summary>
/// Claim cannot be deserialized in default jsonconverter.
/// to use in Microsoft Configuration it can't be derived from Claim, as properties Type and Value are read-only but for reading from configuration they must have set.
///https://stackoverflow.com/questions/28155169/how-to-programmatically-choose-a-constructor-during-deserialization/28156852#28156852
/// </summary>
public class DeserializableClaim
{
public string Type { get; set; }
public string Value { get; set; }
public Claim GetClaim
{
get
{
return new Claim(Type, Value);
}
}
}
Using Azure Durable Functions, I am trying to use the context.GetInput<model>() function which returns the specified model. The model being used has a parameter that is another model which is a derived class. The model that is outputted from context.GetInput<model>() returns the model with the base class instead of the derived class.
I have checked the $type specified in the context, which shows the derived class, but when checking the outputted model, the result is the base class.
for example:
public class Student{
public Book book {get;set;}
}
public class Textbook:Book {
public string classfor {get;set;}
}
public class Book {
public string title {get;set;}
}
[ActivityTrigger] DurableActivityContextBase context is a parameter to the function.
Then I would be calling :
var model = context.GetInput<Student>()
where the context includes
{
"$type": "Student",
"book" : {
"$type": "Textbook",
"classfor" : "Math",
"title" : "PreAlgebra"
}
}
Yet the result is
Model of student which contains a Book instead of Textbook, where the title is assigned "PreAlgebra"
I expect the output of Student model to have a Textbook with properties:
title = "PreAlgebra"
classfor = "Math"
but the actual Student output contains a Book with the property
title = "PreAlgebra"
I've encountered the same problem you did last week. Unfortunately right now Azure Functions (even 2.x) don't support polymorphism for durable functions. The durable context serializes your object to JSON, but there's no way to pass JSON serialization settings as described here on GitHub. There's also another issue about this specific problem.
In my case I have an abstract base class, but you can use the same approach for your derived types. You can create a custom JSON converter that will deal with picking the correct type during deserialization. So for example if you have this sort of inheritance:
[JsonConverter(typeof(DerivedTypeConverter))]
public abstract class Base
{
[JsonProperty("$type")]
public abstract string Type { get; }
}
public class Child : Base
{
public override string Type => nameof(Child);
}
public class Child2 : Base
{
public override string Type => nameof(Child2);
}
Then you can have your а JSON Converter:
public class BaseDerivedTypeConverter : DefaultContractResolver
{
// You need this to protect yourself against circular dependencies
protected override JsonConverter ResolveContractConverter(Type objectType)
{
return typeof(Base).IsAssignableFrom(objectType) && !objectType.IsAbstract
? null
: base.ResolveContractConverter(objectType);
}
}
public class DerivedTypeConverter : JsonConverter
{
private static readonly JsonSerializerSettings Settings =
new JsonSerializerSettings()
{
ContractResolver = new BaseDerivedTypeConverter()
};
public override bool CanConvert(Type objectType) => (objectType == typeof(Base));
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jsonObject = JObject.Load(reader);
// Make checks if jsonObject["$type"].Value<string>() has a supported type
// You can have a static dictionary or a const array of supported types
// You can leverage the array or dictionary to get the type you want again
var type = Type.GetType("Full namespace to the type you want", false); // the false flag means that the method call won't throw an exception on error
if (type != null)
{
return JsonConvert.DeserializeObject(jsonObject.ToString(), type, Settings);
}
else
{
throw new ValidationException("No valid $type has been specified!");
}
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
In my usage when I call context.GetInput<Base>() I can get either Child or Child1 because Base is abstract.
In your case it can be Book or Student depending on what's the actual value. This also applies for other durable function operations like like
var foobar = await context.CallActivityAsync<Base>("FuncName", context.GetInput<int>());
The converter will deal with that and you'll get the object you want inside foobar.
Per my understanding, the class Textbook extends Book, so "Book" is parent class and "Textbook" is subclass. In your context, you want to turn the child class(Textbook) to the parent class(Book). After that, "book" will just have the attribute "title" which is their common attribute but doesn't have the specific attribute "classfor". You can refer to the code below:
Tracked the updates to pass in Json serialization to Azure Functions here showing that it will be in v2.1!
I'm using Newtownsoft JSON.Net and want to add custom attributes, then have a JSONConverter that deals with these custom attributes. Currently the way to do this is [JsonConverter(typeof(CustomJsonConverter))]. But I don't want this converter to always be called, only when I pass it into JsonConvert.SerializeObject(...). This is because this class is being used by two different paths, one of which needs to modify the JSON based on the properties and the other doesn't.
In other words, I only want these attributes to be considered when I explicitly tell my code to consider them. They should be ignored when Newtonsoft is doing its default serialization. How can I achieve this?
Example:
class Foo {
[CustomAttributeToAddMyExtraProperty]
public int Bar;
[JsonProperty('default')]
public int Baz;
}
If I just use the default Newtonsoft JSON.Net serialize, I should get
{
"Bar":value1,
"default":value2
}
But if I pass in my custom converter explicitly, I should get this:
{
"Bar":value1,
"default":value2,
"MyExtraProperty":value3
}
Notice that the JSON.Net attributes are always used.
UPDATE
I have tried this:
namespace Project1
{
class Class1
{
static void Main()
{
Class2 foo = new Class2();
Console.WriteLine(JsonConvert.SerializeObject(foo, new JsonSerializerSettings() { ContractResolver = new BlahResolver() }));
Console.Read();
}
}
class Class2
{
[Blah]
public int one = 1;
[JsonProperty]
public int two = 2;
[Blah]
public string three = "3";
}
internal class BlahAttribute : Attribute
{
}
class BlahResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var attr = property.AttributeProvider.GetAttributes(typeof(BlahAttribute), true);
if (attr.Count == 1)
{
property.Converter = new BlahConverter();
}
return property;
}
}
class BlahConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsValueType;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return existingValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken token = JToken.Parse("{ \"foo\":\"a\", \"bar\":34 }");
token.WriteTo(writer);
}
}
}
Output:
{"one":{"foo":"a","bar":34},"two":2,"three":{"foo":"a","bar":34}}
I'm able to successfully identify properties with my custom attribute and redirect them to my custom converter. The issue now is the converter is adding properties as if one is a complex type. I want to add the properties in the top level JSON, not as properties of one.
Output I want:
{"one":1, "foo":"a","bar":34,"two":2,"three":3,"foo":"a","bar":34}
I realize that this has multiple properties with the same name. I don't know if that's valid or not, but eventually the object value will be included in the name (ex. one_foo, one_bar), so you can disregard that.
With your update, you're very close to getting what you need to work for this scenario. At this point, your only issue is in the WriteJson method.
All you need to do at this point is simply:
writer.WritePropertyName("propertyName");
writer.WriteValue("propertyValue");
If you need to use a different serializer depending on the current execution path, you are correct that the attribute route will not work
Instead pass in the currently desired converter during the serialization process:
string json = JsonConvert.SerializeObject(employee, Formatting.Indented, new MyCustomConverter(typeof(MyType)));
Source
I would like to deserialize a System.Security.Claims.Claim object serialized in the following way:
{
"Issuer" : "LOCAL AUTHORITY",
"OriginalIssuer" : "LOCAL AUTHORITY",
"Type" : "http://my.org/ws/2015/01/identity/claims/mytype",
"Value" : "myvalue",
"ValueType" : "http://www.w3.org/2001/XMLSchema#string"
}
What I get is a JsonSerializationException:
Unable to find a constructor to use for type
System.Security.Claims.Claim. A class should either have a default
constructor, one constructor with arguments or a constructor marked
with the JsonConstructor attribute.
After some investigation I finally understand the meaning of one in the above message: The JSON deserializer cannot find the right constructor as there are - in the case of the Claim type - multiple constructors with arguments (although there exists a constructor with arguments matching exactly the above properties).
Is there a way to tell the deserializer which constructor to choose without adding the JsonConstructor attribute to that mscorlib type?
Daniel Halan has solved this issue with a patch to Json.NET a few years ago. Is there a way to solve this without modifying Json.NET these days?
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;
A ClaimConverter has been packaged with IdentityServer4.
Namespace: IdentityServer4.Stores.Serialization
Example of use:
JsonConvert.DeserializeObject<T>(value, new IdentityServer4.Stores.Serialization.ClaimConverter());
Thanks #chen-zhe, this was the simplest solution among above posts including accepted answer.
In my case, I needed to Deserializing entire Clients (Is4 model) including Claims which was just one of the child class of Client.
Here is what I tried and worked out well -
[HttpPost]
public async Task<ActionResult<Client>> Post(Object model)
{
var clientString = model.ToString();
Client client = JsonConvert.DeserializeObject<Client>(clientString, new ClaimConverter());
}
Things to note to make this work:
First you might want to do away with default binding of framework in my case (ASP.NET Core [FromBody] attribute and
Resolve ClaimCoverter to native Id4 framework library class in IdentityServer4.Stores.Serialization and no need to reinvent the wheel creating your custom JsonConverter suggested above or even .NET Core Custom Model binding approach
Extending James Thorpe answer we are using DeserializableClaim class to read from appsetting configuration
/// <summary>
/// Claim cannot be deserialized in default jsonconverter.
/// to use in Microsoft Configuration it can't be derived from Claim, as properties Type and Value are read-only but for reading from configuration they must have set.
///https://stackoverflow.com/questions/28155169/how-to-programmatically-choose-a-constructor-during-deserialization/28156852#28156852
/// </summary>
public class DeserializableClaim
{
public string Type { get; set; }
public string Value { get; set; }
public Claim GetClaim
{
get
{
return new Claim(Type, Value);
}
}
}