我有一个自定义的JsonConverter
,它似乎没有被正确调用。我已经创建了转换器,将其添加到JsonSerializerSettings.Converters
集合,并标记了与[JsonConverter(typeof(SearchGeoConverter))]
串行的实体上的属性,但即使有了这些转换器,CanConvert
方法也永远看不到我试图转换的类型。我只见过string
、int
和JObject
。
我的转换器看起来像这样:
public class SearchGeoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(DbGeography).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var point = (DbGeography) value;
var rawJson = string.Format("{{ "type": "Point", "coordinates": [{0}, {1}] }}", point.Latitude, point.Longitude);
writer.WriteRaw(rawJson);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
我错过了什么?
CanConvert
在用[JsonConverter]
标记某个内容时不会被调用。当您使用该属性时,Json.Net假设您已经提供了正确的转换器,因此它不需要进行CanConvert
检查。如果删除该属性,那么它将通过将转换器实例传递给设置而被调用。您看到的是Json.Net正在测试您的转换器的所有其他属性类型。
编辑
我整理了一个快速的小提琴来展示我的意思(为了完整起见,下面也转载了代码)。
在不更改程序的情况下,CanConvert()
在FooConverter
上被调用,用于除Foo
之外的所有类型,但它仍然正确地转换Foo
。
如果注释掉Wrapper.Foo
属性上的[JsonConverter]
属性,您可以看到,由于JsonSerializerSettings
中包含FooConverter
,CanConvert()
现在将被调用为类型Foo
。
如果您注释掉Main
中FooConverter
添加到设置中的行,则CanConvert
永远不会为任何类型调用,但由于Wrapper
类中Foo
属性应用了[JsonConverter]
属性,因此Foo
仍然可以正确转换。
因此,这里的要点是,有两种机制可以指示是否应该使用转换器,而不需要两者。您可以应用一个属性,这将告诉Json.Net,特定的转换器应该用于特定的属性(或类),它不需要首先询问转换器。或者,您可以将转换器添加到设置中,在这种情况下,Json.Net必须询问每个转换器是否可以处理每种类型。前者效率更高,而后者在您不拥有要转换的类的源代码的情况下很有用。希望这是有道理的。
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
public class Program
{
public static void Main()
{
JsonSerializerSettings settings = new JsonSerializerSettings();
// Comment out the following line and CanConvert() never gets called on
// FooConverter for any type yet the FooConverter is still working due
// to the JsonConverter attribute applied to Wrapper.Foo
settings.Converters.Add(new FooConverter());
settings.Converters.Add(new BarConverter());
settings.Formatting = Formatting.Indented;
Wrapper w = new Wrapper
{
Foo = new Foo
{
A = "bada",
B = "boom",
},
Bar = new Bar
{
C = "bada",
D = "bing"
}
};
string json = JsonConvert.SerializeObject(w, settings);
Console.WriteLine(json);
}
class Wrapper
{
// Comment out this attribute and CanConvert will be called on FooConverter
// for type Foo due to the fact that the FooConverter has been added to the
// JsonSerializerSettings
[JsonConverter(typeof(FooConverter))]
public Foo Foo { get; set; }
public Bar Bar { get; set; }
}
class Foo
{
public string A { get; set; }
public string B { get; set; }
}
class Bar
{
public string C { get; set; }
public string D { get; set; }
}
class FooConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
bool result = typeof(Foo).IsAssignableFrom(objectType);
Console.WriteLine("FooConverter CanConvert() called for type " +
objectType.Name + " (result = " + result + ")");
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var foo = (Foo) value;
JObject jo = new JObject();
jo.Add("AplusB", new JValue(foo.A + " " + foo.B));
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
class BarConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
bool result = typeof(Bar).IsAssignableFrom(objectType);
Console.WriteLine("BarConverter CanConvert() called for type " +
objectType.Name + " (result = " + result + ")");
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var bar = (Bar) value;
JObject jo = new JObject();
jo.Add("CplusD", new JValue(bar.C + " " + bar.D));
jo.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}