我想将 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();
}
}
现在,写入部分对我来说非常简单(我认为?)。当序列化程序遍历对象时,如果遇到 CanConvert 方法以 true 响应的对象,它会让我的自定义序列化程序将其转换为 JSON。这行得通。
我真正不明白的是另一种方式。因为当它只是用 JSON 中的文字编写时无法知道它是什么类型,我认为我必须自己分析对象并确定它是否实际上是 Vector 对象。 我写的代码有效,但我不知道如果检查失败该怎么办。我如何告诉反序列化器这不是我知道如何翻译的对象之一,它应该按照默认的方式进行翻译?
我是否遗漏了整个事情的运作方式?
最佳答案
在反序列化过程中,Json.Net 会查看您要反序列化到的类,以确定要创建的类型,并进而确定是否调用您的转换器。因此,如果您反序列化为具有 Vector4
属性的类,您的转换器将被调用。如果您反序列化为诸如 dynamic
或 object
或 JObject
之类的模糊内容,那么 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 是一个 Vector4
)。
至于问题的第二部分,如果您的转换器收到的 JSON 不是它所期望的,该怎么办。您有两个选择——返回 null,就像您正在做的那样,或者抛出异常(例如 JsonSerializationException)。如果您的转换器被调用,您就知道 Json.Net 正在尝试填充 Vector4
对象。如果不是,则不会调用您的转换器。因此,如果您因为 JSON 错误而无法填充它,则必须决定是否可以接受 Vector4
为 null,还是最好出错。这是一个设计决策,取决于您在项目中尝试做什么。
我解释清楚了吗?
关于c# - 第三方类型的 Json.NET 自定义序列化/反序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20623565/