[英]Problem (crash) with newtonsoft json deserializing
I am trying to serialize and deserialize a polymorphic type hierarchy using a custom JsonConverter
along the lines of the ones shown in answers to How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?我正在尝试按照如何在 JSON.NET 中实现自定义 JsonConverter 以反序列化基本 class 对象列表中显示的行,使用自定义
JsonConverter
序列化和反序列化多态类型层次结构? . . However, when I call the
ReadJson()
method of the converter to deserialize some JSON I previously serialized, it crashes.但是,当我调用转换器的
ReadJson()
方法来反序列化我之前序列化的一些 JSON 时,它崩溃了。 How can I use the converter to deserialize my JSON?如何使用转换器反序列化我的 JSON?
The following code reproduces the problem.以下代码重现了该问题。 It is a simplification of the original code with only one subtype in the polymorphic type hierarchy.
它是对原始代码的简化,在多态类型层次结构中只有一个子类型。
using System;
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace testJSON
{
class Program
{
public List< Ente > ListaEntes = new List< Ente >();
static void Main(string[] args)
{
Program program = new Program();
program.ListaEntes.Add( new Enemy( 10 ) );
program.ListaEntes.Add( new Enemy( 20 ) );
JsonSerializer serializer = new JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.Objects;
serializer.Formatting = Formatting.Indented;
string folder = "C:\\Users\\pablo\\PasotaPV8_data\\archivoPrueba.dat";
StreamWriter sw = new StreamWriter( @folder );
JsonWriter writer = new JsonTextWriter( sw );
serializer.Serialize( writer, program.ListaEntes );
writer.Close();
sw.Close();
program.ListaEntes.Clear();
StreamReader sr = new StreamReader( @folder );
JsonEnteConverter jsonEnteConverter = new JsonEnteConverter();
JsonReader reader = new JsonTextReader( sr );
program.ListaEntes = ( List< Ente > ) jsonEnteConverter.ReadJson( reader, null, null, serializer );
}
}
public class Ente
{
public string tipo;
public Animator animator;
}
public class Enemy : Ente
{
public Enemy()
{
animator = new Animator( this );
}
public Enemy( int pVar )
{
tipo = "enemigo";
}
}
public class Animator
{
Ente father;
public Animator( Enemy pEnemy )
{
father = pEnemy;
}
}
public class JsonEnteConverter : Newtonsoft.Json.Converters.CustomCreationConverter< Ente >
{
public override Ente Create( Type objectType )
{
throw new NotImplementedException();
}
public Ente Create( JObject jObject )
{
string type = jObject.Property( "tipo" ).ToString(); //get property Type from your json
switch ( type )
{
case "enemigo":
return new Enemy();
}
throw new ApplicationException(String.Format("Type not found", type));
}
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{
JObject jObject = JObject.Load( reader );
var targetObject = Create( jObject );
serializer.Populate( jObject.CreateReader(), targetObject );
return targetObject;
}
}
}
And the error:和错误:
Unhandled exception.
未处理的异常。 Newtonsoft.Json.JsonReaderException: Error reading JObject from JsonReader.
Newtonsoft.Json.JsonReaderException:从 JsonReader 读取 JObject 时出错。 Current JsonReader item is not an object: StartArray.
当前的 JsonReader 项目不是 object:StartArray。 Path '', line 1, position 1. at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings) at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader) at testJSON.JsonEnteConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) in C:\proyectos\proyectosC#\bugJSON\Program.cs:line 90 at testJSON.Program.Main(String[] args) in C:\proyectos\proyectosC#\bugJSON\Program.cs:line 35
Path '', line 1, position 1. at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader, JsonLoadSettings settings) at Newtonsoft.Json.Linq.JObject.Load(JsonReader reader) at testJSON.JsonEnteConverter.ReadJson(JsonReader reader , Type objectType, Object existingValue, JsonSerializer serializer) in C:\proyectos\proyectosC#\bugJSON\Program.cs:line 90 at testJSON.Program.Main(String[] args) in C:\proyectos\proyectosC#\bugJSON\程序.cs:第 35 行
Demo fiddle reproducing the problem here: https://dotnetfiddle.net/cbjYMw .演示小提琴在此处重现问题: https://dotnetfiddle.net/cbjYMw 。
You have a few problems here:你在这里有几个问题:
Your basic problem is that you are attempting to deserialize a List<Ente>
by directly calling JsonEnteConverter.ReadJson()
, however JsonEnteConverter
is designed to deserialize a single instance of Ente
, not a collection of them.您的基本问题是您试图通过直接调用
JsonEnteConverter.ReadJson()
来反序列化List<Ente>
,但是JsonEnteConverter
旨在反序列化Ente
的单个实例,而不是它们的集合。 This causes the exception you are seeing.这会导致您看到的异常。
Instead, you need to add JsonEnteConverter
to JsonSerializerSettings.Converters
, manufacture a JsonSerializer
from the settings, then use that to deserialize a List<Ente>
as follows:相反,您需要将
JsonEnteConverter
添加到JsonSerializerSettings.Converters
,从设置中制造一个JsonSerializer
,然后使用它来反序列化List<Ente>
,如下所示:
var readSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects, Converters = { new JsonEnteConverter() }, // FIXED }; using (var sr = new StreamReader( @folder )) // FIXED dispose of readers properly using (var reader = new JsonTextReader( sr )) { ListaEntes = JsonSerializer.Create(readSettings).Deserialize<List<Ente>>(reader); }
In JsonEnteConverter.Create()
you attempt to check the value of the "tipo"
property by calling jObject.Property( "tipo" ).ToString();
在
JsonEnteConverter.Create()
中,您尝试通过调用jObject.Property( "tipo" ).ToString();
检查"tipo"
属性的值。 . . However,
JObject.Property(string)
returns the JProperty
of the specified name corresponding to the combined name/value pair.但是,
JObject.Property(string)
返回与组合名称/值对对应的指定名称的JProperty
。 Thus the string value evaluates to "tipo": "enemigo"
.因此,字符串值的计算结果为
"tipo": "enemigo"
。
Instead you need to get just the value by doing var type = (string)jObject["tipo"]
:相反,您需要通过执行
var type = (string)jObject["tipo"]
来获取值:
public Ente Create( JObject jObject ) { var type = (string)jObject["tipo"]; // FIXED switch ( type ) { case "enemigo": return new Enemy(); } throw new ApplicationException(String.Format("Type not found", type)); }
StreamWriter
, JsonTextWriter
, StreamReader
and JsonTextReader
are all disposable so should be properly disposed of via a using
statement, eg as shown above. StreamWriter
、 JsonTextWriter
、 StreamReader
和JsonTextReader
都是一次性的,因此应通过using
语句正确处理,例如如上所示。
Since you are using a custom creation converter for the Ente
type hierarchy, you may not need to use TypeNameHandling
.由于您使用的是
Ente
类型层次结构的自定义创建转换器,因此您可能不需要使用TypeNameHandling
。 But if you do, for security reasons you should consider writing a custom ISerializationBinder
for reasons explained in TypeNameHandling caution in Newtonsoft Json .但是,如果这样做,出于安全原因,您应该考虑编写自定义
ISerializationBinder
,原因在 Newtonsoft Json 中的 TypeNameHandling 警告中进行了解释。
Demo fiddle showing the working JsonEnteConverter
here: https://dotnetfiddle.net/VNL5PN .演示小提琴在这里展示了工作的
JsonEnteConverter
: https://dotnetfiddle.net/VNL5PN 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.