简体   繁体   English

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

[英]Json.NET custom serialization/deserialization of a third party type

I want to converts Vectors of the OpenTK library to and from JSON. 我想将OpenTK库的Vector转换为JSON和从JSON转换。 The way I thought it worked is just making a custom JsonConverter, so I did this: 我认为它的工作方式只是制作一个自定义的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();
    }
}

Now, the Write part is pretty straight forward to me (I think?). 现在,Write部分对我很直接(我想?)。 When the serializer goes through objects, if it encounters one that the CanConvert method responds to with true, it lets my custom serializer to convert it to JSON. 当序列化程序遍历对象时,如果遇到CanConvert方法响应为true的对象,它会让我的自定义序列化程序将其转换为JSON。 And that works. 这很有效。

What I don't really get is the other way. 我没有真正得到的是另一种方式。 Since there's no way to know what type something is when it's just written in literals in JSON, I thought I have to analyse the object myself and determine if it's in fact the Vector object. 因为当用JSON中的文字书写时,没有办法知道什么类型的东西,我想我必须自己分析对象并确定它是否实际上是Vector对象。 The code I've written works, but I don't know what to do if the check fails. 我编写的代码有效,但如果检查失败,我不知道该怎么办。 How do I tell the deserializer that this isn't one of the objects that I know how to translate and that it should do it's default thing on it? 我如何告诉反序列化器这不是我知道如何翻译的对象之一,它应该在它上面做默认的事情?

Am I missing something in how the whole thing works? 我错过了整个事情的工作原理吗?

During deserialization, Json.Net looks at the classes that you are deserializing into in order to determine what types to create, and by extension, whether to call your converter. 在反序列化期间,Json.Net会查看要反序列化的类,以确定要创建的类型,以及扩展名,是否调用转换器。 So, if you deserialize into a class that has a Vector4 property, your converter will be called. 因此,如果将反序列化为具有Vector4属性的类,则将调用转换器。 If you deserialize into something nebulous like dynamic or object or JObject , then Json.Net will not know to call your converter, and therefore the deserialized object hierarchy will not contain any Vector4 instances. 如果你反序列化为像dynamicobjectJObject这样模糊不清的东西,那么Json.Net将不知道调用你的转换器,因此反序列化的对象层次结构将不包含任何Vector4实例。

Let's take a simple example to make this concept more clear. 让我们举个简单的例子来说明这个概念。 Say we have this JSON: 假设我们有这个JSON:

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

Clearly, both 'PropA' and 'PropB' in the above JSON could represent a Vector4 (or at least what I infer to be a Vector4 from your converter code-- I am not actually familiar with the OpenTK library). 显然,上面JSON中的'PropA'和'PropB' 都可以代表一个Vector4 (或者至少我推断你的转换器代码中的Vector4我实际上并不熟悉OpenTK库)。 But, as you noticed, there is no type information in the JSON that says that either property should be a Vector4 . 但是,正如您所注意到的,JSON中没有类型信息表明任一属性应该是Vector4

Let's try to deserialize the JSON into the following class using your converter. 让我们尝试使用您的转换器将JSON反序列化到以下类中。 Here, PropA must contain a Vector4 or null since it is strongly typed, while PropB could be anything. 这里, PropA必须包含一个Vector4或null,因为它是强类型的,而PropB可以是任何东西。

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

Here is the test code: 这是测试代码:

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

When I run the test code, here is the output I get: 当我运行测试代码时,这是我得到的输出:

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

So you can see, for PropA , Json.Net used the converter to create the Vector4 instance (otherwise we would have gotten a JsonSerializationException), while for PropB , it did not (otherwise, we would have seen PropB is a Vector4 in the output). 所以你可以看到,对于PropA ,Json.Net使用转换器来创建Vector4实例(否则我们会得到一个JsonSerializationException),而对于PropB ,它没有(否则,我们会看到PropB is a Vector4输出中PropB is a Vector4 )。

As for the second part of your question, what to do if your converter is given JSON that is not what it is expecting. 至于你问题的第二部分,如果你的转换器被赋予了不符合预期的JSON,该怎么办。 You have two choices-- return null, like you are doing, or throw an exception (such as a JsonSerializationException). 你有两个选择 - 像你一样返回null,或抛出异常(例如JsonSerializationException)。 If your converter is being called, you know that Json.Net is trying to populate a Vector4 object. 如果正在调用您的转换器,您知道Json.Net正在尝试填充Vector4对象。 If it were not, then your converter would not have been called. 如果不是,那么你的转换器就不会被调用。 So, if you can't populate it because the JSON is wrong, you have to decide whether it is acceptable that the Vector4 be null, or is it better to error out. 因此,如果由于JSON错误而无法填充它,则必须确定Vector4是否为空是否可接受,或者错误输出是否更好。 It is a design decision that depends on what you are trying to do in your project. 这是一个设计决策,取决于您在项目中尝试做什么。

Have I explained that clearly? 我清楚地解释了吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM