Uchendu Nwachuku
Uchendu Nwachuku

Reputation: 450

Is there a way to make Json.Net deserialize to a concrete type for a property declared as "object"

Let's say I have a JSON document that looks like this:

{
    "Id" : "233124",
    "RequestDate" : "2019-11-25T10:00:00"
    "RequestPayload" : {
        "Id" : "123abc",
        "Url" : "http://blah.example/api/action",
        "PostData" : "insert random post data here"
    }
}

And I have a couple of C# POCOs that look like this:

public class RequestLog {
    public string Id { get; set; }
    public DateTime RequestDate { get; set; }
    public object RequestPayload { get; set; }
}

public class RequestPayload {
    public string Id { get; set; }
    public string Url { get; set; }
    public string PostData { get; set; }
}

Is there a way to have Json.Net deserialize the document such that the RequestLog.RequestPayload property is of type RequestPayload, even though it is declared as object? The goal would be to cast it back to RequestPayload in order to manipulate it, like this:

var result = JsonConvert.DeserializeObject<RequestLog>(json, ...);
var requestPayload = (RequestPayload)request.RequestPayload;
// do other stuff with requestPayload here

I can't change the declaration of RequestLog, as it is in a NuGet package I don't control. I looked into creating a custom converter, and it seems like it should be possible, but I am utterly stumped as to how to pull it off.

Upvotes: 0

Views: 615

Answers (3)

K. Alan Bates
K. Alan Bates

Reputation: 3164

If you must declare this RequestPayload property of the RequestLog as an object, and you don't need this code to be reusable, you can brute force this.

JContainer json = GetMySerializedContentAsJson(content);
RequestPayload payload = JsonConvert.DeserializeObject<RequestPayload>(json["RequestPayload"]);
RequestLog log = JsonConvert.DeserializeObject<RequestLog>(json);
log.RequestPayload = payload;//=>you should now have your object.

Upvotes: 1

Johnathan Barclay
Johnathan Barclay

Reputation: 20373

You can use a custom converter for your RequestLog class:

public class RequestLogConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => objectType == typeof(RequestLog);

    public override bool CanWrite => false;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);

        // Start by running the default serialisation
        var log = new RequestLog();
        serializer.Populate(jObject.CreateReader(), log);

        // Manually deserialize RequestPayload
        log.RequestPayload = jObject["RequestPayload"].ToObject<RequestPayload>();

        return log;
    }

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

Which can be used as follows:

JsonConvert.DeserializeObject<RequestLog>(json, new JsonSerializerSettings
{
    Converters = new JsonConverter[] { new RequestLogConverter() }
});

Upvotes: 3

ThePerplexedOne
ThePerplexedOne

Reputation: 2950

In your RequestLog class,

Change:

public object RequestPayload { get; set; }

to:

public RequestPayload RequestPayload { get; set; }

And then you'll be able to use it after deserialization, without having to cast it:

var result = JsonConvert.DeserializeObject<RequestLog>(json, ...);
var requestPayload = result.RequestPayload;
// do other stuff with requestPayload here

Upvotes: 0

Related Questions