C# - 将JSON反序列化为ValueTuple

4
我正在尝试将 [{"foo": "1", "bar": false}, {"foo": "2", "bar": false}] 反序列化为类型为 List<(string, bool)> 的数据:
JsonConvert.DeserializeObject<List<(string foo, bool bar)>>(json)  

但是始终会得到默认值列表- (null, false).

我该如何实现正确的反序列化?

P.S. 我对于任何用于此目的的模型/类都不感兴趣。我需要的是确切的值元组。


你能添加你期望的输出JSON吗? - HariHaran
请查看此链接 https://dev59.com/hXM_5IYBdhLWcg3w1G6N 或许可以帮到您。 - demo
4个回答

9
在C# 9中,您可以创建一个record并使用生成的解构函数来创建ValueTuple。我看到您不想声明一个模型,但这是我发现的最接近的方法:
声明记录: private record FooBar(string foo, bool bar); 反序列化和解构: (string foo, bool bar) = JsonConvert.DeserializeObject<FooBar>(json); 或者 var (foo, bar) = JsonConvert.DeserializeObject<FooBar>(json);

6

C#元组功能是用来表示值的集合,而不是实体。

值的名称类似于变量名。与变量名一样,元组值名称只存在于源代码中。

(string foo, bool bar)实际上只是ValueTuple<string, int>,就像(string bar, bool foo)一样:

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

元组的值存储在名为Item1Item2等的字段中。
点击这里这里查看其工作原理。
如果你非常热衷于使用值元组,请自行反序列化。
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")));
}

1

实现这个的一种方法是使用JsonConverter。例如:

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

}

现在您可以按照以下方式使用转换器。
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}");
}

样例输出

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

0

我建议首先将 JSON 转换为模型再进行 反序列化

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

方法一 - 使用 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);
     }
}

方法二 - 使用 LINQ 而不必担心重复项

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

方法3 - 使用LINQ来考虑重复项
Dictionary<string, bool> keyValuePairs = JsonConvert
                .DeserializeObject<IEnumerable<item>>(json)
                .GroupBy(p=>p.foo, StringComparer.OrdinalIgnoreCase)
                .ToDictionary(x => x.First().foo, x => x.First().bar);

方法四 - 使用 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();

如果数据中重复使用了键参数(foo),将会在反序列化为字典时引发异常。 - Vishweshwar Kapse
@VishweshwarKapse,已更改代码以避免异常。 - Krishna Varma
我不想为此创建任何虚拟类。我询问了元组相关的问题。 - anatol
@anatol,检查一下第四种方法 - DeserializeAnonymousType 是否解决了你的问题。 - Krishna Varma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接