C# - deserialize JSON into ValueTuple

十年热恋 提交于 2020-12-26 04:57:15

问题


I'm trying to deserialize [{"foo": "1", "bar": false}, {"foo": "2", "bar": false}] into List<(string, bool)> type:

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

But always get a list of default values - (null, false).

How can I achieve correct deserializing?

P.S. I'm not interested in any model/class for that purpose. I need exactly value tuple instead.


回答1:


The C# tuple feature was created to represent sets of values, not entities.

The names of the values are like the names of variables. Like variable names, tuple value names only exist in source code.

(string foo, bool bar) is, actually, just ValueTuple<string, int>. just like (string bar, bool foo):

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

The tuple values are stored in fields named Item1, Item2 and so on.

See for yourself how it works here.

If you're that keen into using value tuples for that, you'll have to deserialize yourself:

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



回答2:


One way to achieve it would be to use a JsonConverter. For example,

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

}

Now you can use the Converter as following.

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

Sample Output

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



回答3:


In C#9 you can create a record and use the generated deconstructor to create a ValueTuple. I did see that you did not want to declare a model but it is the closest approach I have found:

Declare the record:

private record FooBar(string foo, bool bar);

Deserialize and deconstruct:

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

or

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




回答4:


I suggest first convert the JSON to model and Deserialize the json

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

Method 1 - using foreach

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

Method 2 - using LINQ without worrying about duplicates

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

Method 3 - using LINQ by considering duplicates

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

Method 4 - using DeserializeAnonymousType

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


来源:https://stackoverflow.com/questions/59103002/c-sharp-deserialize-json-into-valuetuple

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!