简体   繁体   中英

C# - deserialize JSON into ValueTuple

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?

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

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

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

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

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

您应该创建对象以反序列化

To deserialize Json in C#, probably this is the best way.

Create Model from json,

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

Use that model to deserialize it, using Newtonsoft Json

List<Model> data = JsonConvert.DeserializeObject<List<Model>>(jsonString);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM