[英]Object List<T> at third level not deserializing using Newtonsoft Json.Net (can't find the solution in the documentation)
我有一个项目,我需要从Http服务器的响应中读取。 答案在Json。 该json的对象图反序列化大部分工作,但最低级别的数组失败,留空。
我在下面创建了可以粘贴到空白测试项目并运行的代码。 唯一的测试失败了,我无法解决原因。 示例Json是顶部的const字符串。
我发现System.Web.Extensions
中的JavaScriptSerializer
确实有效(当我使用List而不是数组时)。 但是,Json.Net等效功能不起作用。 下面的示例中有两个测试,Newtonsoft一个失败,但为什么? 我错过了什么项目的Newtonsoft文档?
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using System.Collections.Generic;
/// <summary>
/// Unit Test project that also includes a reference to System.Web.Extensions.
/// Also includes Newtonsoft from NuGet.
/// The constant `_downloadRootObjectEg` holds the sample json.
/// </summary>
namespace Savaged
{
[TestClass]
public class DownloadDeserialisationTest
{
private const string _downloadRootObjectEg = "{ \"error\": \"\", \"success\": true, \"data\": [{ \"data\": [{ \"TextSearched\": \"New product\", \"TextFound\": \"New product\", \"data \": [{ \"x\": 0.585, \"y\": 0.21496437 }, { \"x\": 0.63666666, \"y\": 0.21496437 }, { \"x\": 0.6933333, \"y\": 0.23515439 } ], \"Page\": 16 }, { \"TextSearched\": \"Expiry\", \"TextFound\": \"Expiry\", \"data \": [{ \"x\": 0.6666667, \"y\": 0.16270784 }, { \"x\": 0.7133333, \"y\": 0.16270784 }, { \"x\": 0.7133333, \"y\": 0.18052256 }, { \"x\": 0.6666667, \"y\": 0.18052256 } ], \"Page\": 39 }, { \"TextSearched\": \"Expiry\", \"TextFound\": \"Expiry\", \"data \": [{ \"x\": 0.47833332, \"y\": 0.6686461 }, { \"x\": 0.52166665, \"y\": 0.6686461 }, { \"x\": 0.52166665, \"y\": 0.6864608 }, { \"x\": 0.47833332, \"y\": 0.6864608 } ], \"Page\": 43 } ], \"context\": { \"FileLocation\": \"Product-09-08-2007.pdf\", \"ID\": 1, \"Type\": \"product\" } }, { \"data\": [{ \"TextSearched\": \"New product\", \"TextFound\": \"New product\", \"data \": [{ \"x\": 0.585, \"y\": 0.21496437 }, { \"x\": 0.63666666, \"y\": 0.21496437 }, { \"x\": 0.6933333, \"y\": 0.23515439 }, { \"x\": 0.6433333, \"y\": 0.23515439 } ], \"Page\": 16 }, { \"TextSearched\": \"Expiry\", \"TextFound\": \"Expiry\", \"data \": [{ \"x\": 0.6666667, \"y\": 0.16270784 }, { \"x\": 0.7133333, \"y\": 0.16270784 }, { \"x\": 0.7133333, \"y\": 0.18052256 }, { \"x\": 0.6666667, \"y\": 0.18052256 } ], \"Page\": 39 } ], \"context\": { \"FileLocation\": \"Product-09-08-2007.pdf\", \"ID\": 1, \"Type\": \"product\" } } ], \"count\": 2 }";
[TestMethod]
public void DeserialiseTest()
{
var downloadRootObject =
JsonConvert.DeserializeObject<DownloadRootObject>(_downloadRootObjectEg);
Assert.IsNotNull(downloadRootObject.Data[0].Data[0].Data, "Why?");
}
[TestMethod]
public void JavaScriptSerializerTest()
{
var downloadRootObject = new System.Web.Script.Serialization.
JavaScriptSerializer().Deserialize<DownloadRootObject>(_downloadRootObjectEg);
Assert.IsNotNull(downloadRootObject.Data[0].Data[0].Data, "Why?");
}
}
#region Concrete implementation
public abstract class RootObjectBase
{
public string Error { get; set; }
public bool Success { get; set; }
}
public class DownloadRootObject : RootObjectBase
{
public DownloadRootObject()
{
Data = new List<WordSearch>();
}
[JsonConstructor]
public DownloadRootObject(List<WordSearch> data)
{
Data = data;
}
public List<WordSearch> Data { get; set; }
public int Count { get; set; }
}
public class WordSearch
{
public WordSearch()
{
Data = new List<Match>();
}
[JsonConstructor]
public WordSearch(Context context, List<Match> data)
{
Context = context;
Data = data;
}
public Context Context { get; set; }
public List<Match> Data { get; set; }
}
public class Context
{
public string FileLocation { get; set; }
public int ID { get; set; }
public string Type { get; set; }
}
public class Match
{
public Match()
{
Data = new List<PointF>();
}
[JsonConstructor]
public Match(List<PointF> data)
{
Data = data;
}
public int Page { get; set; }
// TODO switch this to System.Drawing.PointF
public List<PointF> Data { get; set; }
public string TextSearched { get; set; }
public string TextFound { get; set; }
}
public class PointF
{
public float X { get; set; }
public float Y { get; set; }
}
#endregion
}
非常感谢所有帮助!
从我所看到的,提到的列表没有被反序列化,因为最低级别的“data”属性中有一个尾随空格。
\"data \": [{ \"x\": 0.585, \"y\": 0.21496437 }
但实际应该是:
\"data\": [{ \"x\": 0.585, \"y\": 0.21496437 }
@Redstone基本上有正确的答案(upvoted)。 JSON中最内层的数组键称为"data "
(带有尾随空格),而不仅仅是"data"
。 所以发生的事情是,最里面的列表实际上根本没有反序列化,因为序列化程序无法将JSON中的键与Match
类中的Data
属性相Match
。
至于为什么它在JavaScriptSerializer和Json.Net中“起作用” - 它并不是真的。 你的测试不太相同。 不同之处在于您在每种情况下都使用不同的构造函数。 JavaScriptSerializer不支持[JsonConstructor]
属性,因此它总是调用默认构造函数,这会在代码中创建一个空列表。 Json.Net调用您标记的其他构造函数,该构造函数不会创建空列表。 由于反序列化器无法在该构造函数中找到data
参数的匹配项,因此它会传递空值。 在测试中,您只测试结果列表是否为空,而不是测试是否实际成功检索了任何值。 如果扩展测试,您将看到使用JavaScriptSerializer得到一个空列表,而不是来自JSON的实际数据点。
最好的解决方案是修复您的JSON,使其具有正确的data
密钥而没有尾随空格。 如果您不能这样做(例如,因为您没有JSON),那么您的下一个最佳选择是使用[JsonProperty("data ")]
在Match
类中标记Data
属性。 Json.Net仍会将null传递给构造函数中的data
参数(毕竟,参数名称不能包含空格),但它应该找到并使用公共属性访问器来正确设置列表。 请注意,此解决方案不适用于JavaScriptSerializer,因为它也不支持[JsonProperty]
属性,因此如果您需要使用该序列化程序,则可能需要编写自定义转换器来解决此问题。 Json.Net还支持自定义转换器,因此如果您需要确保使用非null数据参数调用备用构造函数,那么这是另一种选择。 请参阅JSON.net:如何在不使用默认构造函数的情况下反序列化? 有关该方法的更多信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.