newtonsoft json.net-使用值元组键反序列化字典

时间:2019-08-01 21:04:07

标签: c# dictionary json.net

使用值元组键反序列化字典时出现错误。我认为它将元组转换为字符串,然后无法将其反序列化为键:

Newtonsoft.Json.JsonSerializationException
  HResult=0x80131500
  Message=Could not convert string '(1, 2)' to dictionary key type 'System.ValueTuple`2[System.Int32,System.Int32]'. Create a TypeConverter to convert from the string to the key type object. Path 'Types['(1, 2)']', line 1, position 49.
  Source=Newtonsoft.Json
  StackTrace:
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateDictionary(IDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at ConsoleApp.Program.Main(String[] args) in D:\Open Source\JsonSerilization\ConsoleApp\ConsoleApp\Program.cs:line 65

Inner Exception 1:
JsonSerializationException: Error converting value "(1, 2)" to type 'System.ValueTuple`2[System.Int32,System.Int32]'. Path 'Types['(1, 2)']', line 1, position 49.

Inner Exception 2:
ArgumentException: Could not cast or convert from System.String to System.ValueTuple`2[System.Int32,System.Int32].

是否有对此的标准解决方案?

到目前为止,似乎我需要提供一个自定义转换器;看起来很乏味。

更新

这是我要序列化/反序列化的类:

public sealed class Message
{
    [JsonConstructor]
    internal Message()
    { }

    public ISet<(int Id, int AnotherId)> Ids { get; set; }
        = new HashSet<(int Id, int AnotherId)>();

    public Dictionary<(int Id, int AnotherId), int> Types { get; set; }
        = new Dictionary<(int Id, int AnotherId), int>();
}

这是我使用的地方:

var message = new Message();
message.Ids.Add((1, 2));
message.Types[(1, 2)] = 3;

var messageStr = JsonConvert.SerializeObject(message);
var messageObj = JsonConvert.DeserializeObject<Message>(messageStr);

2 个答案:

答案 0 :(得分:1)

就像您已经在OP中提到的那样,TypeConverter在此处对反序列化元组键很有用。但这可能不像看上去那样乏味。

例如,您可以编写一个简单的TypeConverter,如下所示。

public class TupleConverter<T1, T2>: TypeConverter 
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context,CultureInfo culture, object value)
    {
        var elements = Convert.ToString(value).Trim('(').Trim(')').Split(new[] {','},StringSplitOptions.RemoveEmptyEntries);
        return (elements.First(),elements.Last());
    }
}

现在,您可以执行以下操作。

var dictionary = new Dictionary<(string,string),int>
{
   [("firstName1","lastName1")] = 5,
   [("firstName2","lastName2")] = 5
};

TypeDescriptor.AddAttributes(typeof((string, string)),  new TypeConverterAttribute(typeof(TupleConverter<string, string>)));
var json = JsonConvert.SerializeObject(dictionary);
var result = JsonConvert.DeserializeObject<Dictionary<(string,string),string>>(json);

答案 1 :(得分:0)

我可以从您的问题中得知,您以某种方式在json对象中有一个(1, 2),这不是newtonsoft如何序列化元组的,因此,如果没有自定义反序列化程序,它将无法将其序列化。 Newtonsoft将Tuples序列化为{"Item1" : 1, "Item2": 2}。这就是为什么您的代码不起作用的原因。如果无法更改输入,则必须编写自定义解串器,但是我建议将输入更改为标准。此处的代码是如何对元组进行序列化/反序列化:

var dictionary = new Dictionary<string, Tuple<int, int>>();
dictionary.Add("test", new Tuple<int, int>(1, 2));
var serializeObject = JsonConvert.SerializeObject(dictionary);
var deserializeObject = JsonConvert.DeserializeObject<Dictionary<string, Tuple<int, int>>>(serializeObject);

Assert.AreEqual(deserializeObject["test"].Item1, 1);