Json.NET自定义序列化/反序列化第三方类型

时间:2013-12-17 00:12:37

标签: c# json.net

我想将OpenTK库的Vector转换为JSON和从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对象。 我编写的代码有效,但如果检查失败,我不知道该怎么办。我如何告诉反序列化器这不是我知道如何翻译的对象之一,并且应该对它进行默认操作?

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

1 个答案:

答案 0 :(得分:4)

在反序列化期间,Json.Net查看要反序列化的类,以确定要创建的类型,以及扩展名,是否调用转换器。因此,如果将反序列化为具有Vector4属性的类,则将调用转换器。如果你反序列化为像dynamicobjectJObject那样模糊不清的东西,那么Json.Net将不知道调用你的转换器,因此反序列化的对象层次结构将不包含任何{{1实例。

让我们举一个简单的例子来说明这个概念。假设我们有这个JSON:

Vector4

显然,上述JSON 中的'PropA'和'PropB'都可以代表{ "PropA": [ 1.0, 2.0, 3.0, 4.0 ], "PropB": [ 5.0, 6.0, 7.0, 8.0 ] } (或者至少从我的转换器中推断为Vector4代码 - 我实际上并不熟悉OpenTK库)。但是,正如您所注意到的,JSON中没有类型信息表明任何属性都应该是Vector4

让我们尝试使用您的转换器将JSON反序列化到以下类中。在此处,Vector4必须包含PropA或null,因为它是强类型的,而Vector4可以是任何内容。

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 ,Json.Net使用转换器来创建PropA实例(否则我们会得到一个JsonSerializationException),而对于Vector4,它做了不(否则,我们会在输出中看到PropB。)

至于问题的第二部分,如果你的转换器被赋予了不符合预期的JSON,该怎么办。你有两个选择 - 像你一样返回null,或抛出异常(例如JsonSerializationException)。如果正在调用您的转换器,您知道Json.Net正在尝试填充PropB is a Vector4对象。如果不是,那么你的转换器就不会被调用。因此,如果由于JSON错误而无法填充它,则必须确定Vector4是否为空是否可接受,或者错误更好。这是一个设计决策,取决于您在项目中尝试做什么。

我清楚地解释了吗?

相关问题