Десериализовать json с отсутствующим конструктором по умолчанию в классе в C#, используя JSON. NET - PullRequest
1 голос
/ 19 марта 2020

Я пытаюсь deserialize строка в объект. Проблема в том, что я хочу десериализовать с помощью конструктора по умолчанию, но его нет в классе. У класса есть только один конструктор с параметром. И мне не разрешено менять класс. Мой сценарий выглядит примерно так:

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());
    }
}

Трассировка стека:

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

Я обнаружил, что если я добавлю конструктор по умолчанию private в класс ConnectionSummary и добавлю ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor в JsonSerializerSettings, могу решить проблему, но я не могу этого сделать. Есть ли что-нибудь еще, что я могу сделать здесь? URL скрипты

Ответы [ 2 ]

3 голосов
/ 19 марта 2020

Вы можете обойти эту проблему, создав пользовательский JsonConverter для класса ConnectionSummary, например:

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();
    }
}

Затем десериализовать так:

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

Fiddle: https://dotnetfiddle.net/U4UR3o

1 голос
/ 19 марта 2020

В этом случае вы можете вызвать конструктор самостоятельно и передать экземпляр преобразователю:

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