如果有一组泛型类型,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; }
}
答案 0 :(得分:1)
您的问题在于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>>();
}