嵌套的 JObjects 被序列化为空数组
Posted
技术标签:
【中文标题】嵌套的 JObjects 被序列化为空数组【英文标题】:Nested JObjects getting serialized as empty arrays 【发布时间】:2020-07-14 16:25:36 【问题描述】:我遇到了一个非常奇怪的情况,我试图将第三方 API 返回的对象序列化为 JSON。我无法控制第三方 API 或其返回的对象。我试图序列化的 C# POCO 看起来像这样:
public class JobSummary
public Job Job get; set;
public class Job
public Status Status get; set;
public class Status
public object JobOutput get; set;
public int Progress get; set;
根据第三方库返回的内容,我希望它会序列化为以下内容。在运行时,我可以看出JobOutput
的类型是JObject
,它包含一个键(Count)和值(0)。
job:
status:
jobOutput:
Count: 0
,
progress: 100
在这里,工作和状态显然是对象。 progress 是 int
,jobOutput 是 JObject
。
如果我运行以下任何变体:
JToken.FromObject(jobSummary)
JObject.FromObject(jobSummary)
JObject.Parse(jobSummary)
而ToString()
或JsonConvert.SerializeObject()
结果,我得到以下输出:
job:
status:
jobOutput:
Count: []
,
progress: 100
请注意,Count 已变为 []
。
但是如果我执行jobSummary.Status.JobOutput.ToString()
,我会正确返回 0,因此我知道第三方库返回的 POCO 没有格式错误并且具有我需要的信息。
有人知道会发生什么吗?或者如何正确序列化嵌套的 JObject?
编辑:我应该澄清一下,由于我无法控制的原因,我使用的是 Newtonsoft v6.0.8,并且包含 POCO 的第三方程序集包含一个未知版本的 Newtonsoft ILMerged。我不知道这是否相关。
【问题讨论】:
如果问题是版本造成的,可以使用最新版本,并在.conf
:<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="6.0.0.0" />
中重定向到旧版本
@sajid 不幸的是,由于我正在使用的更大应用程序的架构,这是不可能的。:(另外我什至不确定问题是由版本造成的。
好的,你有object
序列化的例子吗?
@Sajid 我问题中的第一个 JSON 代表我要序列化的对象。
我们需要看到问题中的“POCO”....否则我们如何才能弄清楚发生了什么?
【参考方案1】:
你写的
我应该澄清一下,由于我无法控制的原因,我使用的是 Newtonsoft v6.0.8,并且包含 POCO 的第三方程序集包含一个未知版本的 Newtonsoft ILMerged。
这解释了您的问题。 JobOutput
包含一个全名为 Newtonsoft.Json.Linq.JObject
的对象来自与您正在使用的完全不同的 Json.NET DLL。当您的 Json.NET 版本测试以查看被序列化的对象是否为 JToken
时,它会检查 objectType.IsSubclassOf(typeof(JToken))
- 这将失败,因为 ILMerged 类型实际上不是您版本类型的子类,尽管有同名。
作为一种解决方法,您需要创建自定义 JsonConverter
逻辑,该逻辑使用外部 ToString()
方法生成输出 JSON,然后将该 JSON 写入您正在生成的 JSON 流。以下应该可以完成这项工作:
public class ForeignJsonNetContainerConverter : ForeignJsonNetBaseConverter
static readonly string [] Names = new []
"Newtonsoft.Json.Linq.JObject",
"Newtonsoft.Json.Linq.JArray",
"Newtonsoft.Json.Linq.JConstructor",
"Newtonsoft.Json.Linq.JRaw",
;
protected override IReadOnlyCollection<string> TypeNames get return Names;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var json = value.ToString();
// Fix indentation
using (var stringReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(stringReader))
writer.WriteToken(jsonReader);
public class ForeignJsonNetValueConverter : ForeignJsonNetBaseConverter
static readonly string [] Names = new []
"Newtonsoft.Json.Linq.JValue",
;
protected override IReadOnlyCollection<string> TypeNames get return Names;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
var underlyingValue = ((dynamic)value).Value;
if (underlyingValue == null)
writer.WriteNull();
else
// JValue.ToString() will be wrong for DateTime objects, we need to serialize them instead.
serializer.Serialize(writer, underlyingValue);
public abstract class ForeignJsonNetBaseConverter : JsonConverter
protected abstract IReadOnlyCollection<string> TypeNames get;
public override bool CanConvert(Type objectType)
if (objectType.IsPrimitive)
return false;
// Do not use the converter for Native JToken types, only non-native types with the same name(s).
if (objectType == typeof(JToken) || objectType.IsSubclassOf(typeof(JToken)))
return false;
var fullname = objectType.FullName;
if (TypeNames.Contains(fullname))
return true;
return false;
public override bool CanRead get return false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
throw new NotImplementedException();
然后在设置中使用它们如下:
var settings = new JsonSerializerSettings
Converters =
new ForeignJsonNetContainerConverter(), new ForeignJsonNetValueConverter()
,
;
var json = JsonConvert.SerializeObject(summary, Formatting.Indented, settings);
注意事项:
转换器的工作原理是假设 FullName
与 Json.NET 类型名称匹配的类型实际上是来自不同版本的 Json.NET 类型。
JValue.ToString()
返回 DateTime
对象的本地化值(有关详细信息,请参阅 here),因此我为 JValue
创建了一个单独的转换器。
我还修复了缩进以匹配。
样机小提琴here.
【讨论】:
我最终单独提出了一个类似的解决方案,但您的解决方案更加彻底和通用。这正是问题的最终结果,谢谢!以上是关于嵌套的 JObjects 被序列化为空数组的主要内容,如果未能解决你的问题,请参考以下文章
iOS JSON 序列化空数组序列化为 nil 而不是 [ ]
使用 Jackson 将嵌套数组反序列化为 ArrayList