Un serveur renvoie une valeur de chaîne JSON qui est une chaîne de requête URL:
{
"parameters": "key1=value1&key2=value2"
}
J'ai une propriété configurée pour recevoir cela et le convertir en Dictionary
dans le cadre du processus de désérialisation:
Propriété avec l'attribut JsonConverter
:
[JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public Dictionary<string, string> Parameters { get; set; }
Convertisseur:
public class QueryStringToDictionaryJsonConverter : JsonConverter<Dictionary<string, string>> {
public override Dictionary<string, string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
var queryString = reader.GetString();
if (string.IsNullOrEmpty(queryString)) return null;
return QueryHelpers.ParseQuery(queryString).ToDictionary(e => e.Key, e => string.Join(",", e.Value.ToArray()));
}
...
}
Cela devrait fonctionner.
Mais ça n'arrive même pas à mon convertisseur.
D'après ce que je peux dire, JsonSerializer.DeserializeAsync<T>(myJson)
voit que le type de propriété est un Dictionary
, et donc il essaie d'analyser la valeur en tant que telle par lui-même, et échoue (l'exception résultante est un 'cast invalide' alors qu'il essaie de GetEnumerable()
etc). Un point d'arrêt dans mon convertisseur n'est même jamais touché.
Je peux le faire fonctionner en faisant de la propriété un object
puis en le convertissant en Dictionary
plus tard où il est utilisé, mais c'est une solution laide.
Existe-t-il un moyen de forcer JsonSerializer.DeserializeAsync<T>(myJson)
à simplement utiliser mon convertisseur, sans qu'il essaie d'être intelligent par lui-même?
(J'utilise System.Text.Json de Microsoft dans .NET Core 3)
OK, cela pourrait donc être un bogue dans System.Text.Json
.
Voici la solution de contournement que j'utilise actuellement pour quiconque a besoin d'une solution.
Tout d'abord, j'ai configuré deux propriétés pour la désérialisation, en utilisant [JsonPropertyName]
et [JsonIgnore]
:
[JsonPropertyName("parameters"), JsonConverter(typeof(QueryStringToDictionaryJsonConverter))]
public object ParametersObject { get; set; }
[JsonIgnore]
public Dictionary<string, string> Parameters => ParametersObject as Dictionary<string, string>;
Et puis dans le JsonConverter
, j'autorise object
comme type:
public override bool CanConvert(Type typeToConvert) {
if (typeToConvert == typeof(object)) return true;
return base.CanConvert(typeToConvert);
}
Les consommateurs de ma classe désérialisée utilisent simplement la propriété Parameters
, qui continuera à bien fonctionner si et quand ce bogue est corrigé et je change la classe comme je le voudrais.
Je créerais un wrapper et créerais un convertisseur pour le wrapper.
[JsonConverter( typeof( QueryStringDictionaryConverter ) )]
class QueryStringDictionary : Dictionary<string,string> { }
class QueryStringDictionaryConverter : JsonConverter<QueryStringDictionary>
{
...
}
class MyClass
{
public QueryStringDictionary Parameters { get; set; }
}
Vous pouvez également utiliser JsonSerializerOptions
class MyOtherClass
{
public Dictionary<string,string> Parameters { get; set; }
}
MyOtherClass Deserialize( string json )
{
var options = new JsonSerializerOptions
{
Converters = { new QueryStringToDictionaryJsonConverter() }
};
return JsonSerializer.Deserialize<MyOtherClass>( json, options );
}
Un problème potentiel avec cette approche est que le convertisseur serait utilisé sur tous les Dictionary<string,string>
propriétés, qui peuvent ne pas être voulues. Cela fonctionnerait bien pour l'exemple simple de la question d'origine.