如何(de)序列化需要自定义JsonConverter的类型列表?

时间:2014-02-21 00:11:02

标签: c# json xna json.net

我有一个需要序列化/反序列化的类。它看起来像这样:

public class Animation
{
    [JsonProperty]
    private readonly string name;

    [JsonProperty]
    private readonly IList<Rectangle> frames;
}

然而,Json.Net与XNA的Rectangle类并不相称。它需要一个自定义JsonConverter,我managed to scrape together。这适用于具有单个Rectangle属性的类,例如:

public class Sprite
{
    [JsonConverter(typeof(RectangleConverter))]
    [JsonProperty]
    private readonly Rectangle frame;
}

但是如何将该转换器应用到我Rectangle班级的Animation列表中?

2 个答案:

答案 0 :(得分:1)

根据the doc,XNA Rectangle结构标记为TypeConverter,默认情况下类型为转换器always return true when asked to convert to string。因此,Json.Net默认会尝试创建字符串契约而不是对象契约,这会在与RectangleConverter一起使用时导致错误。这可以通过使用自定义ContractResolver来告诉Json.Net将Rectangle视为对象而不是字符串。我们还可以在解析器中设置转换器,因此您无需在[JsonConverter]处使用Rectangle属性修饰类属性。

以下是您需要的代码:

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);
    }
}

要使用解析器,请将其添加到序列化设置并将其传递给DeserializeObject()SerializeObject(),如下所示:

JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new CustomResolver();

Animation anim = JsonConvert.DeserializeObject<Animation>(json, settings);

答案 1 :(得分:1)

希望有人为我提供更好的解决方案(必须有一个),但这对我现在很有用:

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();
    }
}

我必须修改我正在使用的RectangleConverter

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;
    }
}

具有新JsonConverter属性的动画类:

public class Animation
{
    [JsonProperty]
    private readonly string name;

    [JsonConverter(typeof(RectangleListConverter))]
    [JsonProperty]
    private readonly IList<Rectangle> frames;
}