J’ai un object avec une liste de sous-objects de classe de base. Les sous-objects ont besoin d’un convertisseur personnalisé. Je ne parviens pas à faire en sorte que mon convertisseur personnalisé respecte l’option ItemTypeNameHandling
.
Exemple de code (créez un nouveau projet de console C #, ajoutez le package JSON.NET NuGet):
using System; using System.Collections.Generic; using Newtonsoft.Json; namespace My { class Program { private static void Main () { Console.WriteLine(JsonConvert.SerializeObject( new Box { toys = { new Spintop(), new Ball() } }, Formatting.Indented)); Console.ReadKey(); } } [JsonObject] class Box { [JsonProperty ( ItemConverterType = typeof(ToyConverter), ItemTypeNameHandling = TypeNameHandling.Auto)] public List toys = new List(); } [JsonObject] class Toy {} [JsonObject] class Spintop : Toy {} [JsonObject] class Ball : Toy {} class ToyConverter : JsonConverter { public override void WriteJson (JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } public override object ReadJson (JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return serializer.Deserialize(reader, objectType); } public override bool CanConvert (Type objectType) { return typeof(Toy).IsAssignableFrom(objectType); } } }
Production produite:
{ "toys": [ {}, {} ] }
Sortie nécessaire (c’est ce qui arrive si je commente ItemConverterType = typeof(ToyConverter),
line):
{ "toys": [ { "$type": "My.Spintop, Serialization" }, { "$type": "My.Ball, Serialization" } ] }
J’ai essayé de changer temporairement la valeur de serializer.TypeNameHandling
dans la méthode ToyConverter.WriteJson
, mais cela affecte des propriétés non liées. (Bien entendu, mon vrai convertisseur est plus complexe que cela. Ce n’est qu’un exemple avec des fonctionnalités de base.)
Question: Comment faire en sorte que mon JsonConverter
personnalisé respecte la propriété JsonProperty
atsortingbut JsonProperty
?
Après avoir exploré le code source de Json.Net (version 4.5 édition 11), il semble que ce que vous voulez faire ne soit pas possible.
La clé pour obtenir des types écrits dans la sortie est cette méthode:
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter .ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Ce sont les parameters containerContract
et containerProperty
qui sont importants ici. Lors de la sérialisation sans convertisseur, ces parameters sont fournis et ShouldWriteType
peut les utiliser pour déterminer le TypeNameHandling
à utiliser.
Lors de la sérialisation avec un convertisseur, cependant, ces deux parameters ne sont pas fournis. Cela semble être dû au fait que ToyConverter.WriteJson
aboutit à un appel à Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue
comme Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue
:
SerializeValue(jsonWriter, value, GetContractSafe(value), null, null, null);
Notez que les deux derniers parameters sont en fait JsonContainerContract containerContract
et JsonProperty containerProperty
et sont transmis à la méthode ShouldWriteType
. C’est là que réside le problème: comme ils sont nuls, la logique de la méthode ShouldWriteType
signifie qu’elle renvoie false
, le type n’est donc pas écrit.
Modifier:
Inspiré par cela , vous pouvez résoudre ce problème en personnalisant la méthode WriteJson
de votre convertisseur:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteStartObject(); writer.WritePropertyName("$type"); writer.WriteValue(RemoveAssemblyDetails(value.GetType().AssemblyQualifiedName.ToSsortingng())); writer.WriteEndObject(); } private static ssortingng RemoveAssemblyDetails(ssortingng fullyQualifiedTypeName) { SsortingngBuilder builder = new SsortingngBuilder(); // loop through the type name and filter out qualified assembly details from nested type names bool writingAssemblyName = false; bool skippingAssemblyDetails = false; for (int i = 0; i < fullyQualifiedTypeName.Length; i++) { char current = fullyQualifiedTypeName[i]; switch (current) { case '[': writingAssemblyName = false; skippingAssemblyDetails = false; builder.Append(current); break; case ']': writingAssemblyName = false; skippingAssemblyDetails = false; builder.Append(current); break; case ',': if (!writingAssemblyName) { writingAssemblyName = true; builder.Append(current); } else { skippingAssemblyDetails = true; } break; default: if (!skippingAssemblyDetails) builder.Append(current); break; } } return builder.ToString(); }
Notez que cette méthode RemoveAssemblyDetails
est extraite directement de la source Json.Net.
Vous devrez bien sûr modifier la méthode WriteJson
pour afficher le rest des champs, mais j'espère que cela suffira.