Reputation: 16056
I have a class that needs serializing/deserializing. It looks like this:
public class Animation
{
[JsonProperty]
private readonly string name;
[JsonProperty]
private readonly IList<Rectangle> frames;
}
However, Json.Net doesn't play nice with XNA's Rectangle
class. It needs a custom JsonConverter
, which I managed to scrape together. This works fine on classes with a single Rectangle
property, like:
public class Sprite
{
[JsonConverter(typeof(RectangleConverter))]
[JsonProperty]
private readonly Rectangle frame;
}
But how do I apply that converter to the list of Rectangle
s in my Animation
class?
Upvotes: 3
Views: 2766
Reputation: 129787
According to the doc, the XNA Rectangle
struct is marked as having a TypeConverter
, and by default type converters always return true when asked to convert to string. Because of this Json.Net will by default try to create a string contract instead of an object contract, which will lead to an error when used with your RectangleConverter. This can be fixed by using a custom ContractResolver
to tell Json.Net that Rectangle
should be treated as an object, not a string. We can also set the converter inside the resolver, so you do not need to decorate your class properties with a [JsonConverter]
attribute wherever you use a Rectangle
.
Here is the code you would need:
class CustomResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (objectType == typeof(Rectangle) || objectType == typeof(Rectangle?))
{
JsonContract contract = base.CreateObjectContract(objectType);
contract.Converter = new RectangleConverter();
return contract;
}
return base.CreateContract(objectType);
}
}
To use the resolver, add it to the serializer settings and pass that to DeserializeObject()
or SerializeObject()
like this:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new CustomResolver();
Animation anim = JsonConvert.DeserializeObject<Animation>(json, settings);
Upvotes: 2
Reputation: 16056
Hopefully someone comes up with a better solution for me (there's got to be one), but this is working for me right now:
public class RectangleListConverter : RectangleConverter
{
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{
var rectangleList = (IList<Rectangle>)value;
var jArray = new JArray();
foreach ( var rectangle in rectangleList )
{
jArray.Add( GetObject( rectangle ) );
}
jArray.WriteTo( writer );
}
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{
var rectangleList = new List<Rectangle>();
var jArray = JArray.Load( reader );
foreach ( var jToken in jArray )
{
rectangleList.Add( GetRectangle( jToken ) );
}
return rectangleList;
}
public override bool CanConvert( Type objectType )
{
throw new NotImplementedException();
}
}
I had to modify the RectangleConverter
I was using:
public class RectangleConverter : JsonConverter
{
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{
var rectangle = (Rectangle)value;
var jObject = GetObject( rectangle );
jObject.WriteTo( writer );
}
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{
var jObject = JObject.Load( reader );
return GetRectangle( jObject );
}
public override bool CanConvert( Type objectType )
{
throw new NotImplementedException();
}
protected static JObject GetObject( Rectangle rectangle )
{
var x = rectangle.X;
var y = rectangle.Y;
var width = rectangle.Width;
var height = rectangle.Height;
return JObject.FromObject( new { x, y, width, height } );
}
protected static Rectangle GetRectangle( JObject jObject )
{
var x = GetTokenValue( jObject, "x" ) ?? 0;
var y = GetTokenValue( jObject, "y" ) ?? 0;
var width = GetTokenValue( jObject, "width" ) ?? 0;
var height = GetTokenValue( jObject, "height" ) ?? 0;
return new Rectangle( x, y, width, height );
}
protected static Rectangle GetRectangle( JToken jToken )
{
var jObject = JObject.FromObject( jToken );
return GetRectangle( jObject );
}
protected static int? GetTokenValue( JObject jObject, string tokenName )
{
JToken jToken;
return jObject.TryGetValue( tokenName, StringComparison.InvariantCultureIgnoreCase, out jToken ) ? (int)jToken : (int?)null;
}
}
Animation class with new JsonConverter
attribute:
public class Animation
{
[JsonProperty]
private readonly string name;
[JsonConverter(typeof(RectangleListConverter))]
[JsonProperty]
private readonly IList<Rectangle> frames;
}
Upvotes: 1