【问题标题】:Json.NET custom serialization/deserialization of a third party type第三方类型的 Json.NET 自定义序列化/反序列化
【发布时间】:2013-12-17 11:58:33
【问题描述】:

我想将 OpenTK 库的向量与 JSON 相互转换。我认为它的工作方式只是制作一个自定义 JsonConverter,所以我这样做了:

class VectorConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Vector4);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = JToken.Load(reader);
        if (obj.Type == JTokenType.Array)
        {
            var arr = (JArray)obj;
            if (arr.Count == 4 && arr.All(token => token.Type == JTokenType.Float))
            {
                return new Vector4(arr[0].Value<float>(), arr[1].Value<float>(), arr[2].Value<float>(), arr[3].Value<float>());
            }
        }
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var vector = (Vector4)value;
        writer.WriteStartArray();
        writer.WriteValue(vector.X);
        writer.WriteValue(vector.Y);
        writer.WriteValue(vector.Z);
        writer.WriteValue(vector.W);
        writer.WriteEndArray();
    }
}

现在,Write 部分对我来说非常简单(我认为?)。当序列化程序遍历对象时,如果遇到 CanConvert 方法以 true 响应的对象,它会让我的自定义序列化程序将其转换为 JSON。这行得通。

我没有真正得到的是另一种方式。由于没有办法知道当它只是用 JSON 中的文字编写时它是什么类型,所以我认为我必须自己分析该对象并确定它是否实际上是 Vector 对象。 我编写的代码有效,但如果检查失败,我不知道该怎么办。我如何告诉反序列化器这不是我知道如何翻译的对象之一,它应该做它的默认操作?

我是否遗漏了整个事情的运作方式?

【问题讨论】:

    标签: c# json.net


    【解决方案1】:

    在反序列化期间,Json.Net 会查看您要反序列化的类,以确定要创建的类型,以及是否调用转换器。因此,如果您反序列化为具有Vector4 属性的类,您的转换器将被调用。如果您反序列化为 dynamicobjectJObject 之类的模糊对象,那么 Json.Net 将不知道调用您的转换器,因此反序列化的对象层次结构将不包含任何 Vector4 实例。

    让我们举一个简单的例子,让这个概念更清楚。假设我们有这个 JSON:

    {
        "PropA": [ 1.0, 2.0, 3.0, 4.0 ],
        "PropB": [ 5.0, 6.0, 7.0, 8.0 ]
    }
    

    显然,上述 JSON 中的“PropA”和“PropB”都可以代表Vector4(或者至少我从您的转换器代码中推断为Vector4——我我实际上并不熟悉 OpenTK 库)。但是,正如您所注意到的,JSON 中没有类型信息表明任一属性都应该是 Vector4

    让我们尝试使用您的转换器将 JSON 反序列化为以下类。这里,PropA 必须包含 Vector4 或 null,因为它是强类型的,而 PropB 可以是任何东西。

    public class Tester
    {
        public Vector4 PropA { get; set; }
        public object PropB { get; set; }
    }
    

    这里是测试代码:

    class Program
    {
        static void Main(string[] args)
        {
            string json = @"
            {
                ""PropA"": [ 1.0, 2.0, 3.0, 4.0 ],
                ""PropB"": [ 5.0, 6.0, 7.0, 8.0 ]
            }";
    
            try
            {
                Tester t = JsonConvert.DeserializeObject<Tester>(json),
                                                  new VectorConverter());
    
                DumpObject("PropA", t.PropA);
                DumpObject("PropB", t.PropB);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
            }
        }
    
        static void DumpObject(string prop, object obj)
        {
            if (obj == null)
            {
                Console.WriteLine(prop + " is null");
            }
            else
            {
                Console.WriteLine(prop + " is a " + obj.GetType().Name);
                if (obj is Vector4)
                {
                    Vector4 vector = (Vector4)obj;
                    Console.WriteLine("   X = " + vector.X);
                    Console.WriteLine("   Y = " + vector.Y);
                    Console.WriteLine("   Z = " + vector.Z);
                    Console.WriteLine("   W = " + vector.W);
                }
                else if (obj is JToken)
                {
                    foreach (JToken child in ((JToken)obj).Children())
                    {
                        Console.WriteLine("   (" + child.Type + ") " 
                                                 + child.ToString());
                    }
                }
            }
        }
    }
    
    // Since I don't have the OpenTK library, I'll use the following class
    // to stand in for `Vector4`.  It should look the same to your converter.
    
    public class Vector4
    {
        public Vector4(float x, float y, float z, float w)
        {
            X = x;
            Y = y;
            Z = z;
            W = w;
        }
    
        public float W { get; set; }
        public float X { get; set; }
        public float Y { get; set; }
        public float Z { get; set; }
    }
    

    当我运行测试代码时,这是我得到的输出:

    PropA is a Vector4
       X = 1
       Y = 2
       Z = 3
       W = 4
    PropB is a JArray
       (Float) 5
       (Float) 6
       (Float) 7
       (Float) 8
    

    所以你可以看到,对于PropA,Json.Net 使用转换器创建了 Vector4 实例(否则我们会得到 JsonSerializationException),而对于 PropB,它没有(否则,我们会在输出中看到PropB is a Vector4)。

    至于您问题的第二部分,如果您的转换器收到的 JSON 不是它所期望的,该怎么办。你有两个选择——返回 null,就像你正在做的那样,或者抛出一个异常(比如 JsonSerializationException)。如果您的转换器被调用,您就知道 Json.Net 正在尝试填充 Vector4 对象。如果不是,那么您的转换器将不会被调用。因此,如果您因为 JSON 错误而无法填充它,您必须决定是否可以接受 Vector4 为空,还是最好出错。这是一个设计决策,取决于您在项目中尝试做什么。

    我解释清楚了吗?

    【讨论】:

    • 你有,这很有意义。非常感谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 2017-03-19
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多