2014-09-24 11 views
8

Mam niestandardowy JsonConverter, który nie wydaje się być poprawnie wywoływany. Stworzyłem konwerter, dodałem go do kolekcji JsonSerializerSettings.Converters i zaznaczyłem właściwość na obiekcie, który szereguję z [JsonConverter(typeof(SearchGeoConverter))], ale nawet z tymi w miejscu, w metodzie konwerterów CanConvert nigdy nie widzę typu, który próbuję przekonwertować. Widzę tylko, string, int i JObject.JsonConverter CanConvert nie otrzymuje typu

Moja konwerter wygląda następująco:

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

Czego mi brakuje?

+0

modyfikator klasy jest publiczny? – codebased

+0

Tak, zarówno encja, jak i konwerter są publiczne. – ilivewithian

Odpowiedz

12

CanConvert nie zostanie wywołany, gdy oznaczysz coś za pomocą [JsonConverter]. Kiedy używasz tego atrybutu, Json.Net zakłada, że ​​podałeś poprawny konwerter, więc nie przejmuje się sprawdzaniem CanConvert. Jeśli usuniesz atrybut, zostanie on wywołany na podstawie przekazania instancji konwertera do ustawień. To, co widzisz, to Json.Net testujący twój konwerter dla wszystkich innych typów nieruchomości.

EDIT

ułożyła szybki fiddle aby pokazać o co mi chodzi (kod jest również przedstawione poniżej dla kompletności).

bez zmian programu CanConvert() jest wywoływana na FooConverter dla wszystkich typów wyjątkiemFoo, a mimo to nadal przetwarza Foo poprawnie.

Jeśli wykomentuj atrybut [JsonConverter] na nieruchomości Wrapper.Foo, można zobaczyć, że CanConvert() będzie teraz sprawdzony na typ Foo mocy FooConverter wchodzących w JsonSerializerSettings.

Jeśli zamiast zakomentuj linię Main gdzie FooConverter jest dodawana do ustawień, a następnie CanConvert nigdy nie jest nazywany dla każdego typu, jeszcze Foo nadal poprawnie konwertowane z powodu atrybutu [JsonConverter] stosowanej do nieruchomości Foo w Wrapper klasa.

Tak więc na wynos jest to, że istnieją dwa mechanizmy wskazujące, czy konwerter powinien być używany, i nie potrzebujesz obu. Możesz zastosować atrybut, który powie Json.Net, że konkretny konwerter powinien być używany dla określonej właściwości (lub klasy) i nie musi najpierw pytać konwertera. Alternatywnie możesz dodać konwerter do ustawień, w takim przypadku Json.Net musi zapytać każdego konwertera, czy może obsłużyć każdy typ. Ten pierwszy jest nieco bardziej wydajny, a drugi jest przydatny w sytuacjach, gdy nie posiadasz kodu źródłowego klasy, którą próbujesz przekonwertować. Mam nadzieję, że to ma sens.

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