C# 反序列化数组中的项时忽略自定义JsonConverter
编辑:制作了一个更简单、更透明的示例案例 我正在尝试反序列化组件数组(属于实体)。 其中一个组件是Sprite组件,它保存纹理和动画信息。我已经为此实现了一个CustomConverter,因为原始的sprite类有点臃肿,也没有无参数构造函数(该类来自一个单独的库,所以我无法修改它) 实际用例有点复杂,但我在下面添加了一个类似的示例。我测试了代码,同样的问题也出现了。反序列化时从不使用ReadJson。但是,当序列化WriteJson时,会被称为“非常好” 这些是组件和它的自定义转换器C# 反序列化数组中的项时忽略自定义JsonConverter,c#,json,serialization,json.net,deserialization,C#,Json,Serialization,Json.net,Deserialization,编辑:制作了一个更简单、更透明的示例案例 我正在尝试反序列化组件数组(属于实体)。 其中一个组件是Sprite组件,它保存纹理和动画信息。我已经为此实现了一个CustomConverter,因为原始的sprite类有点臃肿,也没有无参数构造函数(该类来自一个单独的库,所以我无法修改它) 实际用例有点复杂,但我在下面添加了一个类似的示例。我测试了代码,同样的问题也出现了。反序列化时从不使用ReadJson。但是,当序列化WriteJson时,会被称为“非常好” 这些是组件和它的自定义转换器 pu
public class ComponentSample
{
int entityID;
}
public class Rect
{
public int x;
public int y;
public int width;
public int height;
public Rect( int x, int y, int width, int height )
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
//this class would normally have a texture and a bunch of other data that is hard to serialize
//so we will use a jsonconverter to instead point to an atlas that contains the texture's path and misc data
public class SpriteSample<TEnum> : ComponentSample
{
Dictionary<TEnum, Rect[]> animations = new Dictionary<TEnum, Rect[]>();
public SpriteSample( TEnum animationKey, Rect[] frames )
{
this.animations.Add( animationKey, frames );
}
}
public class SpriteSampleConverter : JsonConverter<SpriteSample<int>>
{
public override SpriteSample<int> ReadJson( JsonReader reader, Type objectType, SpriteSample<int> existingValue, bool hasExistingValue, JsonSerializer serializer )
{
JObject jsonObj = JObject.Load( reader );
//get texturepacker atlas
string atlasPath = jsonObj.Value<String>( "atlasPath" );
//some wizardy to get all the animation and load the texture and stuff
//for simplicity sake I'll just put in some random data
return new SpriteSample<int>( 99, new Rect[ 1 ] { new Rect( 0, 0, 16, 16 ) } );
}
public override void WriteJson( JsonWriter writer, SpriteSample<int> value, JsonSerializer serializer )
{
writer.WriteStartObject();
writer.WritePropertyName( "$type" );
//actually don't know how to get the type, so I just serialized the SpriteSample<int> to check
writer.WriteValue( "JsonSample.SpriteSample`1[[System.Int32, mscorlib]], NezHoorn" );
writer.WritePropertyName( "animationAtlas" );
writer.WriteValue( "sampleAtlasPathGoesHere" );
writer.WriteEndObject();
}
}
但当我尝试反序列化列表时,它从不在SpriteSampleConverter上调用ReadJson,而是尝试按原样反序列化对象
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
settings.PreserveReferencesHandling = PreserveReferencesHandling.All;
settings.TypeNameHandling = TypeNameHandling.All;
settings.Formatting = Formatting.Indented;
settings.MissingMemberHandling = MissingMemberHandling.Ignore;
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
settings.Converters.Add( new SpriteSampleConverter() );
using ( StreamReader file = File.OpenText( "sample.json" ) )
{
//JsonConvert.PopulateObject( file.ReadToEnd(), man, settings );
JObject componentsJson = JObject.Parse( file.ReadToEnd() );
//ComponentList components = JsonConvert.DeserializeObject<ComponentList>( componentsJson.ToString(), settings );
JArray array = JArray.Parse( componentsJson.GetValue( "$values" ).ToString() );
ComponentSample[] list = JsonConvert.DeserializeObject<ComponentSample[]>( array.ToString(), settings );
//The SpriteSampleConverter does work here!
SpriteSample<int> deserializedSprite = JsonConvert.DeserializeObject<SpriteSample<int>>( componentsJson.GetValue( "$values" ).ElementAt(2).ToString(), settings );
}
JsonSerializerSettings设置=新建JsonSerializerSettings();
settings.ReferenceLoopHandling=Newtonsoft.Json.ReferenceLoopHandling.Ignore;
settings.PreserveReferencesHandling=PreserveReferencesHandling.All;
settings.typenameholling=typenameholling.All;
settings.Formatting=格式化.缩进;
settings.MissingMemberHandling=MissingMemberHandling.Ignore;
settings.NullValueHandling=NullValueHandling.Ignore;
settings.DefaultValueHandling=DefaultValueHandling.Ignore;
settings.ConstructorHandling=ConstructorHandling.AllowNonPublicDefaultConstructor;
settings.converter.Add(新的SpriteSampleConverter());
使用(StreamReader file=file.OpenText(“sample.json”))
{
//JsonConvert.PopulateObject(file.ReadToEnd(),man,settings);
JObject componentsJson=JObject.Parse(file.ReadToEnd());
//ComponentList components=JsonConvert.DeserializeObject(ComponentJSON.ToString(),设置);
JArray数组=JArray.Parse(componentsJson.GetValue(“$values”).ToString();
ComponentSample[]list=JsonConvert.DeserializeObject(array.ToString(),settings);
//SpriteSampleConverter在这里工作!
SpriteSample deserializedSprite=JsonConvert.DeserializeObject(componentsJson.GetValue($values”).ElementAt(2.ToString(),设置);
}
我做了一个快速测试,看看SpriteSampleConverter是否工作,ReadJson确实在这里被调用
SpriteSample deserializedSprite=JsonConvert.DeserializeObject>(componentsJson.GetValue($values”).ElementAt(2.ToString(),设置)
这不是有效的解决方案,因为我不知道对象是否/在哪里会有精灵组件。
我猜反序列化组件[]会让序列化程序只使用default转换器吗?
知道我做错了什么吗
编辑
我刚刚尝试了一个非泛型JsonConverter,以查看是否调用了CanConvert,令人惊讶的是,在检查类型ComponentSample[]和ComponentSample时调用了它,但SpriteSample从未通过检查
public class SpriteSampleConverterTwo : JsonConverter
{
public override bool CanConvert( Type objectType )
{
return objectType == typeof( SpriteSample<int> );
}
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{
JObject jsonObj = JObject.Load( reader );
//get texturepacker atlas
string atlasPath = jsonObj.Value<String>( "atlasPath" );
//some wizardy to get all the animation and load the texture and stuff
//for simplicity sake I'll just put in some random data
return new SpriteSample<int>( 99, new Rect[ 1 ] { new Rect( 0, 0, 16, 16 ) } );
}
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{
writer.WriteStartObject();
writer.WritePropertyName( "$type" );
//actually don't know how to get the type, so I just serialized the SpriteSample<int> to check
writer.WriteValue( "JsonSample.SpriteSample`1[[System.Int32, mscorlib]], NezHoorn" );
writer.WritePropertyName( "animationAtlas" );
writer.WriteValue( "sampleAtlasPathGoesHere" );
writer.WriteEndObject();
}
}
公共类SpriteSampleConverterTwo:JsonConverter
{
公共覆盖布尔CanConvert(类型objectType)
{
返回objectType==typeof(SpriteSample);
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
JObject jsonObj=JObject.Load(读卡器);
//获取纹理贴图器atlas
字符串atlasPath=jsonObj.Value(“atlasPath”);
//获得所有动画并加载纹理和内容的一些技巧
//为了简单起见,我只需要输入一些随机数据
返回新的SpriteSample(99,new Rect[1]{new Rect(0,0,16,16)});
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
writer.WriteStartObject();
writer.WritePropertyName($type);
//实际上我不知道如何获取类型,所以我只是序列化了SpriteSample以进行检查
WriteValue(“JsonSample.SpriteSample`1[[System.Int32,mscorlib]],NezHoorn”);
writer.WritePropertyName(“animationAtlas”);
WriteValue(“SampleAtlaspathGoesher”);
writer.WriteEndObject();
}
}
我希望我能看看json.net的源代码,但我现在遇到了一大堆麻烦
让它运行。我查看了json.net源代码,得出结论,只有数组声明的类型才会用于检查转换器
JsonConverter collectionItemConverter = GetConverter(contract.ItemContract, null, contract, containerProperty);
int? previousErrorIndex = null;
bool finished = false;
do
{
try
{
if (reader.ReadForType(contract.ItemContract, collectionItemConverter != null))
{
switch (reader.TokenType)
{
case JsonToken.EndArray:
finished = true;
break;
case JsonToken.Comment:
break;
default:
object value;
if (collectionItemConverter != null && collectionItemConverter.CanRead)
{
value = DeserializeConvertable(collectionItemConverter, reader, contract.CollectionItemType, null);
}
它不会检查每个单独数组项的类型以查找相应的转换器
因此,我需要找到一种不同于使用转换器的解决方案。您能尝试使用简单的标准POCOs将其归结为一种解决方案吗?事实上,如果没有
Sprite
的定义,我们真的没有希望帮助您。例如,您是否尝试反序列化其中一个多态子类型应用了自定义JsonConverter
的多态对象集合?谢谢您的建议,我将编辑示例。是的,这正是我想要做的!
public class SpriteSampleConverterTwo : JsonConverter
{
public override bool CanConvert( Type objectType )
{
return objectType == typeof( SpriteSample<int> );
}
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{
JObject jsonObj = JObject.Load( reader );
//get texturepacker atlas
string atlasPath = jsonObj.Value<String>( "atlasPath" );
//some wizardy to get all the animation and load the texture and stuff
//for simplicity sake I'll just put in some random data
return new SpriteSample<int>( 99, new Rect[ 1 ] { new Rect( 0, 0, 16, 16 ) } );
}
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{
writer.WriteStartObject();
writer.WritePropertyName( "$type" );
//actually don't know how to get the type, so I just serialized the SpriteSample<int> to check
writer.WriteValue( "JsonSample.SpriteSample`1[[System.Int32, mscorlib]], NezHoorn" );
writer.WritePropertyName( "animationAtlas" );
writer.WriteValue( "sampleAtlasPathGoesHere" );
writer.WriteEndObject();
}
}
JsonConverter collectionItemConverter = GetConverter(contract.ItemContract, null, contract, containerProperty);
int? previousErrorIndex = null;
bool finished = false;
do
{
try
{
if (reader.ReadForType(contract.ItemContract, collectionItemConverter != null))
{
switch (reader.TokenType)
{
case JsonToken.EndArray:
finished = true;
break;
case JsonToken.Comment:
break;
default:
object value;
if (collectionItemConverter != null && collectionItemConverter.CanRead)
{
value = DeserializeConvertable(collectionItemConverter, reader, contract.CollectionItemType, null);
}