无法反序列化泛型类型的集合

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无法反序列化泛型类型的集合相关的知识,希望对你有一定的参考价值。

如果存在泛型类型的集合,Json.net不会反序列化它先前创建的JSON。如何正确反序列化此类JSON?

这是我试图反序列化的JSON:

{  
    "$type":"MyProject.Messages.ChangeMsg`1[[MyProject.Classes.DTO.DeviceDTO, MyProject]], MyProject",
    "ChangedDataList":{  
        "$type":"System.Collections.Generic.List`1[[System.Tuple`2[[MyProject.Classes.DTO.DeviceDTO, MyProject],[MyProject.Enums.ChangedStatus, MyProject]], mscorlib]], mscorlib",
        "$values":[  
            {  
                "$type":"System.Tuple`2[[MyProject.Classes.DTO.DeviceDTO, MyProject],[MyProject.Enums.ChangedStatus, MyProject]], mscorlib",
                "Item1":{  
                    "$type":"MyProject.Classes.DTO.SwitchDeviceDTO, MyProject",
                    "Id":318,
                    "Name":"Device",
                    "ios":{  
                        "$type":"System.Collections.Generic.List`1[[MyProject.Classes.DTO.IoDTO, MyProject]], mscorlib",
                        "$values":[  

                        ]
                    },
                    "GuiProperties":{  
                        "$type":"System.Collections.Generic.List`1[[MyProject.Classes.DTO.GuiPropertiesDTO, MyProject]], mscorlib",
                        "$values":[  
                            {  
                                "$type":"MyProject.Classes.DTO.GuiPropertiesDTO, MyProject",
                                "Id":319,
                                "X":200,
                                "Y":0,
                                "DeviceId":318,
                                "ChangedStatus":0
                            }
                        ]
                    },
                    "ChangedStatus":0
                },
                "Item2":0
            }
        ]
    }
}

我在输出窗口中看不到任何错误,但在反序列化后,ChangedDataList始终是null

这里是反序列化代码:

private static T GetMessage<T>(string msg)
{
    JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
    return JsonConvert.DeserializeObject<T>(msg, settings);
}

这里是一个完整的LinqPad示例:

void Main()
{
    ChangeMsg<DeviceDTO> chgMsg = new ChangeMsg<DeviceDTO>(new List<Tuple<DeviceDTO, ChangedStatus>>() { new Tuple<DeviceDTO, ChangedStatus>(new DeviceDTO() { Id = 318, Name = "Device" }, ChangedStatus.New)});

    var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
    string msg = JsonConvert.SerializeObject(chgMsg, settings);

    var obj = JsonConvert.DeserializeObject(msg, settings);
}

// Define other methods and classes here
public enum ChangedStatus
{
    New,
    Modified,
    Deleted
}

[DataContract]
public enum ChangedStatusDTO
{
    [EnumMember]
    New,
    [EnumMember]
    Modified,
    [EnumMember]
    Deleted
}

public interface IChangedStatusDTO
{
    ChangedStatusDTO ChangedStatus { get; set; }
}

public abstract class SdnMessage
{
    public int RequestId { get; set; }
}

[JsonObject(MemberSerialization.OptIn)]
public class ChangeMsg<T> : SdnMessage where T : IChangedStatusDTO
{
    [JsonConstructor]
    public ChangeMsg(List<Tuple<T, ChangedStatus>> tuples)
    {
        ChangedDataList = tuples;
    }

    [JsonProperty("ChangedDataList")]
    public List<Tuple<T, ChangedStatus>> ChangedDataList { get; }
}

[DataContract(IsReference = true)]
public class ConnectionDTO : IChangedStatusDTO
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public int FromId { get; set; }

    [DataMember]
    public int? FromDeviceId { get; set; }

    [DataMember]
    public int ToId { get; set; }

    [DataMember]
    public int? ToDeviceId { get; set; }

    [DataMember]
    public ChangedStatusDTO ChangedStatus { get; set; }
}

[DataContract]
public class GuiPropertiesDTO : IChangedStatusDTO
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public int X { get; set; }
    [DataMember]
    public int Y { get; set; }
    [DataMember]
    public int Z { get; set; }
    [DataMember]
    public int Width { get; set; }
    [DataMember]
    public int Height { get; set; }
    [DataMember]
    public int? DeviceId { get; set; }

    [DataMember]
    public ChangedStatusDTO ChangedStatus { get; set; }
}

[DataContract(IsReference = true)]
public class IoDTO : IChangedStatusDTO
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public int Number { get; set; }
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public string Description { get; set; }
    [DataMember]
    public int? DeviceId { get; set; }
    [DataMember]
    public virtual ICollection<ConnectionDTO> ConnectionFroms { get; set; }
    [DataMember]
    public virtual ICollection<ConnectionDTO> ConnectionTos { get; set; }
    [DataMember]
    public ChangedStatusDTO ChangedStatus { get; set; }
}

[DataContract(IsReference = true)]
public class DeviceDTO : IChangedStatusDTO
{
    public DeviceDTO()
    {
        Name = string.Empty;
        Ios = new HashSet<IoDTO>();
        GuiProperties = new HashSet<GuiPropertiesDTO>();
    }
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public int ManufacturerId { get; set; }
    [DataMember]
    public virtual ICollection<IoDTO> Ios { get; set; }
    [DataMember]
    public virtual ICollection<GuiPropertiesDTO> GuiProperties { get; set; }

    [DataMember]
    public ChangedStatusDTO ChangedStatus { get; set; }
}
答案

你的问题是ChangeMsg<T>构造函数:

[JsonObject(MemberSerialization.OptIn)]
public class ChangeMsg<T> : SdnMessage where T : IChangedStatusDTO
{
    [JsonConstructor]
    public ChangeMsg(List<Tuple<T, ChangedStatus>> tuples)
    {
        ChangedDataList = tuples;
    }

    [JsonProperty("ChangedDataList")]
    public List<Tuple<T, ChangedStatus>> ChangedDataList { get; }
}

构造函数参数的名称是tuples,它与属性名称"ChangedDataList"不同。因此,当使用"ChangedDataList"属性反序列化JSON时,如问题所示,Json.NET无法知道它应该绑定到tuples参数。这是因为Json.NET将JSON属性绑定到构造函数参数,方法是匹配它们的名称modulo case。而是传入null并且永远不会分配ChangedDataList c#属性。由于此属性是get-only,因此Json.NET随后无法填充它,并且会跳过JSON中的值。

要解决此问题,可以将构造函数参数的名称更改为与属性名称一致。如果传入ArgumentNullException,您还应该分配一个空列表或抛出null

[JsonConstructor]
public ChangeMsg(List<Tuple<T, ChangedStatus>> changedDataList)
{
    this.ChangedDataList = changedDataList ?? new List<Tuple<T, ChangedStatus>>();
}

(我的偏好不是从反序列化代码中抛出ArgumentNullException,但您的偏好可能会有所不同。)

或者,如果您认为序列化依赖于构造函数参数的命名太脆弱,您可以使用[JsonProperty]显式标记构造函数参数,如下所示:

[JsonConstructor]
public ChangeMsg( [JsonProperty("ChangedDataList")] List<Tuple<T, ChangedStatus>> tuples)
{
    this.ChangedDataList = tuples ?? new List<Tuple<T, ChangedStatus>>();
}

以上是关于无法反序列化泛型类型的集合的主要内容,如果未能解决你的问题,请参考以下文章

无法使用泛型使用 Jackson 反序列化动态 json

如何使用 Moshi 反序列化泛型类型?

Gson通过借助TypeToken类来解决这个问题

Gson反序列化泛型类型适配器的基类

Android:Gson通过借助TypeToken获取泛型参数的类型的方法

序列化和反序列化