0
votes

C # - désérialiser JSON en ValueTuple

J'essaye de désérialiser [{"foo": "1", "bar": false}, {"foo": "2", "bar": false}] en type List<(string, bool)> :

JsonConvert.DeserializeObject<List<(string foo, bool bar)>>(json)  

Mais obtenez toujours une liste de valeurs par défaut - (null, false) .

Comment puis-je obtenir une désérialisation correcte?

PS Je ne suis intéressé par aucun modèle / classe à cet effet. J'ai besoin d'un tuple de valeur exactement à la place.


2 commentaires

Pouvez-vous ajouter la sortie json attendue?


vérifiez ce stackoverflow.com/questions/1207731/ ... peut-être que cela peut vous aider


4 Réponses :


0
votes

Je suggère d'abord de convertir le JSON en modèle et de Deserialize le json

 var definition = new[] {new { foo = "", bar = false } };
 string json = @"[{'foo': '1', 'bar': false}, {'foo': '2', 'bar': true}]";
 var obj = JsonConvert.DeserializeAnonymousType(json, definition).Select(p=> (p.foo, p.bar)).ToList();

Méthode 1 - Utilisation de foreach

Dictionary<string, bool> keyValuePairs = JsonConvert
                .DeserializeObject<IEnumerable<item>>(json)
                .GroupBy(p=>p.foo, StringComparer.OrdinalIgnoreCase)
                .ToDictionary(x => x.First().foo, x => x.First().bar);

Méthode 2 - Utiliser LINQ sans se soucier des doublons

Dictionary<string, bool> keyValuePairs = JsonConvert.DeserializeObject<IEnumerable<item>>(json).ToDictionary(x => x.foo, x => x.bar);

Méthode 3 - Utiliser LINQ en tenant compte des doublons

using (StreamReader r = new StreamReader(filepath))
{
     string json = r.ReadToEnd();
     var obj = JsonConvert.DeserializeObject<List<item>>(json);

     Dictionary<string, bool> keyValuePairs = new Dictionary<string, bool>();
     foreach (var keyvalue in obj)
     {
          if (!keyValuePairs.ContainsKey(keyvalue.foo))
             keyValuePairs.Add(keyvalue.foo, keyvalue.bar);
     }
}

Méthode 4 - Utilisation de DeserializeAnonymousType

public class item
{
    public string foo { get; set; }
    public bool bar { get; set; }
}


4 commentaires

La désérialisation vers le dictionnaire provoquera une exception si le paramètre Key (foo) est répété dans les données.


@VishweshwarKapse, ont changé le code pour éviter les exceptions


Je ne souhaite pas créer de classe factice pour cette raison. J'ai posé une question sur les tuples


@anatol, vérifiez si la méthode 4 - DeserializeAnonymousType résout votre problème



1
votes

Une façon d'y parvenir serait d'utiliser un JsonConverter. Par exemple,

foo:1,bar:False
foo:2,bar:False

Vous pouvez maintenant utiliser le convertisseur comme suit.

var json = "[{'foo': '1', 'bar': false}, {'foo': '2', 'bar': false}]";
var result = JsonConvert.DeserializeObject<IEnumerable<(string,bool)>>(json,new ValueTupleConverter<string,bool>());
foreach(var (foo,bar) in result)
{
   Console.WriteLine($"foo:{foo},bar:{bar}");
}

Exemple de sortie

public class ValueTupleConverter<U,V> : Newtonsoft.Json.JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(ValueTuple<U,V>) == objectType;
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader,Type objectType,object existingValue,Newtonsoft.Json.JsonSerializer serializer)
    {
        if (reader.TokenType == Newtonsoft.Json.JsonToken.Null) return null;

        var jObject = Newtonsoft.Json.Linq.JObject.Load(reader);
        var properties = jObject.Properties().ToList();
        return new ValueTuple<U, V>(jObject[properties[0].Name].ToObject<U>(), jObject[properties[1].Name].ToObject<V>());
    }

    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

}


0 commentaires

3
votes

La fonctionnalité de tuple C # a été créée pour représenter des ensembles de valeurs, pas des entités.

Les noms des valeurs sont comme les noms des variables. Comme les noms de variables, les noms de valeurs de tuple n'existent que dans le code source.

(string foo, bool bar) est, en fait, juste ValueTuple<string, int> . tout comme (string bar, bool foo) :

var json = "[{\"foo\": \"1\", \"bar\": false}, {\"foo\": \"2\", \"bar\": false}]";

var jArray = JsonConvert.DeserializeObject<JArray> (json);

var list = new List<(string foo, bool bar)>();

foreach (var item in jArray)
{
    list.Add((item.Value<string>("foo"), item.Value<bool>("bar")));
}

Les valeurs de tuples sont stockées dans des champs nommés Item1 , Item2 et ainsi de suite.

Voyez par vous-même comment cela fonctionne ici .

Si vous souhaitez utiliser des tuples de valeur pour cela, vous devrez vous désérialiser:

(string foo, bool bar) a = ('one', true);
(string bar, bool foo) b = a;


0 commentaires

1
votes

En C # 9, vous pouvez créer un record et utiliser le déconstructeur généré pour créer un ValueTuple. J'ai vu que vous ne vouliez pas déclarer de modèle mais c'est l'approche la plus proche que j'ai trouvée:

Déclarez l'enregistrement:

private record FooBar(string foo, bool bar);

Désérialiser et déconstruire:

(string foo, bool bar) = JsonConvert.DeserializeObject<FooBar>(json);

ou

var (foo, bar) = JsonConvert.DeserializeObject<FooBar>(json);


0 commentaires