2

I am trying to deserialize a string into an object. The problem is I want to deserialize using the default constructor but that is not present in the class. The class has only one constructor with parameter. And I am not allowed to change the class. My scenario is something like this:

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

public class ConnectionSummary
{   
    public ConnectionSummary(Connection connection)
    {
        this.ConnectionId = connection.Id;
        this.SystemId = connection.SystemId;
    }

    [JsonProperty(PropertyName = "connectionId", Required = Required.Always)]
    public string ConnectionId { get; set; }

    [JsonProperty(PropertyName = "systemId")]
    public string SystemId { get; set; }
}


public class Connection
{
    public Connection()
    {
        // Initialization of some properties
    }

    [JsonProperty(PropertyName = "systemId", Required = Required.Always)]
    public string SystemId { get; set; }

    [JsonProperty(PropertyName = "id", Required = Required.Always)]
    public string Id { get; set; }

    // Other properties
}


public class Program
{
    public static void Main()
    {
        var json = "{\"connectionId\":\"id\",\"systemId\":\"sId\"}";
        var deserial = JsonConvert.DeserializeObject<ConnectionSummary>(json); // getting error here.
        Console.WriteLine(deserial.ToString());
    }
}

Stack trace:

Run-time exception (line 43): Exception has been thrown by the target of an invocation.

Stack Trace:

[System.NullReferenceException: Object reference not set to an instance of an object.]
   at ConnectionSummary..ctor(Connection connection) :line 9

[System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.]
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Newtonsoft.Json.Utilities.LateBoundReflectionDelegateFactory.<>c__DisplayClass3_0.<CreateParameterizedConstructor>b__0(Object[] a)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectUsingCreatorWithParameters(JsonReader reader, JsonObjectContract contract, JsonProperty containerProperty, ObjectConstructor`1 creator, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at Program.Main() :line 43

I found that if I add a private default constructor in the ConnectionSummary class and adding ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor in the JsonSerializerSettings, can resolve the issue but I can't do that. Is there any other thing I can do here? Fiddle Url

7
  • Why are you deserialising to ConnectionSummary when you should just be using Connection? Commented Mar 19, 2020 at 16:59
  • Sorry my bad, I should have explained. ConnectionSummary has other properties as well. Commented Mar 19, 2020 at 17:00
  • To give a correct answer here, the context is important. Which part of your process is supposed to call "Deserialize"? Commented Mar 19, 2020 at 17:00
  • But your JSON doesn't match your objects. You have shown us the child object here, Newtonsoft doesn't know how to make a Connection object unless you write a custom converter. Commented Mar 19, 2020 at 17:02
  • Context is something like this: A web API returns Task<ConnectionSummary>. I am writing the tests. I need to call this API and then deserialize the response.Content and then do further checks. Commented Mar 19, 2020 at 17:02

2 Answers 2

4

You can work around the issue by making a custom JsonConverter for the ConnectionSummary class like this:

public class ConnectionSummaryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ConnectionSummary);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        Connection conn = new Connection
        {
            Id = (string)jo["connectionId"],
            SystemId = (string)jo["systemId"]
        };
        return new ConnectionSummary(conn);
    }

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

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

Then deserialize like this:

var deserial = JsonConvert.DeserializeObject<ConnectionSummary>(json, new ConnectionSummaryConverter());

Fiddle: https://dotnetfiddle.net/U4UR3o

Sign up to request clarification or add additional context in comments.

Comments

0

In this case you can call the constructor yourself, and supply the instance to the converter:

var json = "{\"connectionId\":\"id\",\"systemId\":\"sId\"}";
var cs = new ConnectionSummary(new Connection());
Newtonsoft.Json.JsonConvert.PopulateObject(json, cs);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.