JSON.net: comment désérialiser sans utiliser le constructeur par défaut?

j'ai une classe qui a un constructeur par défaut et aussi un constructeur surchargé qui prend un ensemble de paramètres. Ces paramètres correspondent aux Champs sur l'objet et sont assignés à la construction. À ce stade, j'ai besoin du constructeur par défaut pour d'autres buts, donc je voudrais le garder si je peux.

mon problème: si je supprime le constructeur par défaut et passe dans la chaîne JSON, l'objet désérialise correctement et passe dans les paramètres du constructeur sans aucun problème. Je finis par récupérer l'objet peuplé de la façon dont je m'y attendais. Cependant, dès que j'ajoute le constructeur par défaut dans l'objet, quand je l'appelle JsonConvert.DeserializeObject<Result>(jsontext) les propriétés sont les plus peuplées.

à ce point j'ai essayé d'ajouter new JsonSerializerSettings(){CheckAdditionalContent = true} à l'appel de désérialisation. qui n'a rien fait.

autre note. le constructeur paramètres ne correspondent aux noms des champs exactement, sauf que les paramètres sont commencer par une lettre minuscule. Je ne pense pas que cela ait de l'importance puisque, comme je l'ai mentionné, la deserialisation fonctionne très bien sans constructeur par défaut.

Voici un échantillon de mes constructeurs:

    public Result() { }

    public Result(int? code, string format, Dictionary<string, string> details = null)
    {
        Code = code ?? ERROR_CODE;
        Format = format;

        if (details == null)
            Details = new Dictionary<string, string>();
        else
            Details = details;
    }
98
demandé sur kmacdonald 2014-04-11 20:16:59

2 réponses

Json.Net préfère utiliser le constructeur par défaut (parameterless) sur un objet s'il en existe un. S'il y a plusieurs constructeurs et que vous voulez Json.Net pour utiliser un attribut non par défaut, vous pouvez ajouter l'attribut [JsonConstructor] au constructeur que vous voulez. Json.Net pour appeler.

[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
    ...
}

il est important que les noms de paramètres du constructeur correspondent aux noms de propriétés correspondants de L'objet JSON (ignorant le cas) pour que cela fonctionne correctement. Vous n'avez pas nécessairement avoir un paramètre dans le constructeur pour chaque propriété de l'objet, cependant. Pour les propriétés de l'objet JSON qui ne sont pas couvertes par les paramètres du constructeur, Json.Net essaiera d'utiliser les accesseurs de propriété publique (ou les propriétés/champs marqués avec [JsonProperty] ) pour peupler l'objet après sa construction.

si vous ne voulez pas ajouter d'attributs à votre classe ou si vous ne contrôlez pas le code source de la classe que vous essayez de desérialiser, alors une autre alternative est de créer un personnalisé JsonConverter pour instancier et peupler votre objet. Par exemple:

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load the JSON for the Result into a JObject
        JObject jo = JObject.Load(reader);

        // Read the properties which will be used as constructor parameters
        int? code = (int?)jo["Code"];
        string format = (string)jo["Format"];

        // Construct the Result object using the non-default constructor
        Result result = new Result(code, format);

        // (If anything else needs to be populated on the result object, do that here)

        // Return the result
        return result;
    }

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

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

Ensuite, ajoutez le convertisseur à vos paramètres de serializer, et utilisez les paramètres lorsque vous désérialisez:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
140
répondu Brian Rogers 2017-12-13 16:37:57

un peu en retard et pas exactement adapté ici, mais je vais ajouter ma solution ici, parce que ma question avait été fermée comme une copie de celle-ci, et parce que cette solution est complètement différente.

j'avais besoin d'une manière générale pour donner l'instruction Json.NET de préférer le constructeur le plus spécifique pour un type de structure défini par l'utilisateur, de sorte que je puisse omettre les attributs JsonConstructor qui ajouteraient une dépendance au projet où chacune de ces structures est définir.

j'ai rétro-conçu un bit et mis en œuvre un résolveur de contrat personnalisé où j'ai dépassé la méthode CreateObjectContract pour ajouter ma logique de création personnalisée.

public class CustomContractResolver : DefaultContractResolver {

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var c = base.CreateObjectContract(objectType);
        if (!IsCustomStruct(objectType)) return c;

        IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
        var mostSpecific = list.LastOrDefault();
        if (mostSpecific != null)
        {
            c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
            c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
        }

        return c;
    }

    protected virtual bool IsCustomStruct(Type objectType)
    {
        return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
    }

    private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
    {
        method.ThrowIfNull("method");
        var c = method as ConstructorInfo;
        if (c != null)
            return a => c.Invoke(a);
        return a => method.Invoke(null, a);
    }
}

je l'utilise comme ceci.

public struct Test {
  public readonly int A;
  public readonly string B;

  public Test(int a, string b) {
    A = a;
    B = b;
  }
}

var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
  ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");
21
répondu Zoltán Tamási 2017-05-23 12:10:04