Редактировать: упрощенный и более прозрачный пример сэмпла
Я пытаюсь десериализовать массив компонентов (принадлежащих сущности).
Одним из компонентов является компонент Sprite, который содержит информацию о текстуре и анимации. Для этого я реализовал CustomConverter, так как исходный класс спрайтов является раздутым, а также не имеет конструктора без параметров (класс из отдельной библиотеки, поэтому я не могу его изменить).
Реальный вариант использования немного сложнее, но ниже я добавил похожий пример. Я проверил код и такая же проблема возникает. ReadJson никогда не используется при десериализации. Но при сериализации WriteJson вызывается совершенно нормально.
это компоненты и специальный конвертер для него;
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();
}
}
Правильно генерирует Json при сериализации;
JsonSerializer serializer = new JsonSerializer();
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
settings.PreserveReferencesHandling = PreserveReferencesHandling.All; //none
settings.TypeNameHandling = TypeNameHandling.All;
settings.Formatting = Formatting.Indented;
settings.MissingMemberHandling = MissingMemberHandling.Ignore;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
settings.Converters.Add( new SpriteSampleConverter() );
ComponentSample[] components = new ComponentSample[]
{
new ComponentSample(),
new ComponentSample(),
new SpriteSample<int>(10, new Rect[] { new Rect(0,0,32,32 ) } )
};
string fullFile = "sample.json";
string directoryPath = Path.GetDirectoryName( fullFile );
if ( directoryPath != "" )
Directory.CreateDirectory( directoryPath );
using ( StreamWriter file = File.CreateText( fullFile ) )
{
string jsonString = JsonConvert.SerializeObject( components, settings );
file.Write( jsonString );
}
Json:
{
"$id": "1",
"$type": "JsonSample.ComponentSample[], NezHoorn",
"$values": [
{
"$id": "2",
"$type": "JsonSample.ComponentSample, NezHoorn"
},
{
"$id": "3",
"$type": "JsonSample.ComponentSample, NezHoorn"
},
{
"$type": "JsonSample.SpriteSample`1[[System.Int32, mscorlib]], NezHoorn",
"animationAtlas": "sampleAtlasPathGoesHere"
}
]
}
Но когда я пытаюсь десериализовать список, он никогда не вызывает ReadJson на SpriteSampleConverter и просто пытается десериализовать объект как есть.
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 );
}
Я сделал быстрый тест, чтобы увидеть, работает ли SpriteSampleConverter, и здесь вызывается ReadJson;
SpriteSample deserializedSprite = JsonConvert.DeserializeObject> (componentsJson.GetValue ("$ values") .ElementAt (2) .ToString (), настройки);
Это недопустимое решение, хотя я не могу знать, будет ли / где объект иметь спрайтовый компонент.
Я предполагаю, что десериализация в Component [] заставляет сериализатор просто использовать конвертер ошибок?
Есть идеи, что я могу делать не так?
Редактировать
Я только что попробовал не универсальный 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();
}
}
Хотел бы я взглянуть на источник json.net, но у меня куча неприятностей
заставить его работать.