繁体   English   中英

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

[英]newtonsoft json.net - deserializing dictionary with value tuple key

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

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

就像您在 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 key = Convert.ToString(value).Trim('(').Trim(')');
        var parts = Regex.Split(key, (", "));
        var item1 = (T1)TypeDescriptor.GetConverter(typeof(T1)).ConvertFromInvariantString(parts[0])!;
        var item2 = (T2)TypeDescriptor.GetConverter(typeof(T2)).ConvertFromInvariantString(parts[1])!;
        return new ValueTuple<T1, T2>(item1, item2);
    }
}

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

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

我可以从您的问题中得知,您的 json 对象中以某种方式获得了(1, 2) ,这不是 newtonsoft 序列化元组的方式,因此如果没有自定义反序列化器,将无法将其序列化。 Newtonsoft 将元组序列化为{"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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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