使用Json.NET序列化XNA矩形

6

我正在使用Json.NET 首先看看这个:

using System.Drawing;
string json = JsonConvert.SerializeObject(new Rectangle(-3,6,32,32), Formatting.Indented);
Console.WriteLine(json);
Rectangle deserializedRectangle = JsonConvert.DeserializeObject<Rectangle>(json);

一切都按预期运行。控制台输出为:“3、6、32、32”。
但是,当我想使用XNA矩形做同样的事情时,出错了。(只是用这个“using Microsoft.Xna.Framework”替换了旧的using)
控制台输出为:“{X:-3 Y:6 Width:32 Height:32}”,它抛出的错误是:“将值“{X:-3 Y:6 Width:32 Height:32}”转换为类型“Microsoft.Xna.Framework.Rectangle”时出错。”
1.为什么会发生这种情况? 2.出现了什么问题,我该如何解决?

尝试从源代码构建Json.NET,这样您就可以在调试器中查看引发的异常。问题#1的答案:似乎很明显它正在使用 RectangleToString 方法进行转换,而不是提取单个成员值,并且没有方法将其转换回来。我猜测Json.NET使用公共get / set属性(如System.Drawing.Rectangle)并且无法"看到"Microsoft.Xna.Framework.Rectangle的公共字段 - Andrew Russell
3个回答

5

我已经进行了一些检查,发现以下代码引起了异常:

    public static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object convertedValue)
    {
      return MiscellaneousUtils.TryAction<object>(delegate { return Convert(initialValue, culture, targetType); }, out convertedValue);
    }

实际调用执行转换工作的委托无法找到此类型的转换器。调查其原因,因为序列化器能够正确地序列化和反序列化其他类型。
编辑:
这不起作用,因为XNA矩形类型被定义为:
    [Serializable]
    [TypeConverter(typeof(RectangleConverter))]
    public struct Rectangle : IEquatable<Rectangle>

Json.NET会检索TypeConverter类型,并调用其上的此方法:
  TypeConverter fromConverter = GetConverter(targetType);

  if (fromConverter != null && fromConverter.CanConvertFrom(initialType)) 
  {
       // deserialize
  }

RectangleConverter有一个名为"supportsStringConvert=false"的标志,因此尝试将字符串转换为该对象会失败。
这就是反序列化此特定对象失败的原因。

哇,太棒了!感谢你找到这个问题。你有什么想法来解决这个问题,或者有什么方法可以绕过它吗? - Riki
如果json.net允许,您可以尝试将其序列化为不同的对象(而不是字符串)。我明天会查一下。 - lysergic-acid
看起来你需要创建一个简单的DTO来序列化/反序列化XNA矩形结构。 - MattDavey

2

我找到了一种方法,可以让Newtonsoft.Json (Json.Net)与XNA的Rectangle类很好地协作。首先,您的矩形应该是一个类的属性,这样您就可以给它一个JsonConverter属性:

public class Sprite
{
    [JsonConverter(typeof(MyRectangleConverter))]
    public Rectangle Rectangle;
}

public class MyRectangleConverter : JsonConverter
{
    public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
    {
        var rectangle = (Rectangle)value;

        var x = rectangle.X;
        var y = rectangle.Y;
        var width = rectangle.Width;
        var height = rectangle.Height;

        var o = JObject.FromObject( new { x, y, width, height } );

        o.WriteTo( writer );
    }

    public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
    {
        var o = JObject.Load( reader );

        var x = GetTokenValue( o, "x" ) ?? 0;
        var y = GetTokenValue( o, "y" ) ?? 0;
        var width = GetTokenValue( o, "width" ) ?? 0;
        var height = GetTokenValue( o, "height" ) ?? 0;

        return new Rectangle( x, y, width, height );
    }

    public override bool CanConvert( Type objectType )
    {
        throw new NotImplementedException();
    }

    private static int? GetTokenValue( JObject o, string tokenName )
    {
        JToken t;
        return o.TryGetValue( tokenName, StringComparison.InvariantCultureIgnoreCase, out t ) ? (int)t : (int?)null;
    }
}

它可能需要改进,因此欢迎反馈意见。

如何编写自定义转换器以进行JSON序列化 - kot-da-vinci

0

这绝对是我找到的解决此问题的最佳方案:

private class XnaFriendlyResolver : DefaultContractResolver {
  protected override JsonContract CreateContract(Type objectType) {
    // Add additional types here such as Vector2/3 etc.
    if (objectType == typeof(Rectangle)) {
      return CreateObjectContract(objectType);
    }

    return base.CreateContract(objectType);
  }
}

只需配置Newtonsoft.JSON以使用解析器

var settings = new JsonSerializerSettings() {
  ContractResolver = new XnaFriendlyResolver(),
};

var rect = JsonConvert.DeserializeObject<Rectangle>(jsonData, settings);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接