Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Converters specified via Attributes to be equivalent to global ones in json.net

When using Newtonsoft.Json, I can make it do what I need by adding a converter to the top level SerializerSettings or supplying it to the conversion invocation - all is working well.

I'm hoping to extract some of my global converters to instead be applied declaratively in the relevant place where the conversion is actually required.

I'm aware of the following techniques:-

  • type level [JsonConverter(typeof(Converters.StringEnumConverter))] directly on type X
  • member level [JsonConverter(typeof(Converters.StringEnumConverter))] iff the member is of type X
  • item level [JsonProperty(ItemConverterType=typeof(Converters.StringEnumConverter)] if the member is actually an array etc. of X

The problem I'm having is that some of the global converters I have in play operate on nested types, e.g. if I have member of type Tuple<X[],Nullable<X>>, I can't express the "if you meet an X when processing this field or any child of it, do the conversion" semantic and instead get a Newtonsoft.Json.JsonSerializationException.

Does such a "for this tree, also use this converter please" mechanism exist? I'd like to avoid having to define a top level type for anything I ever want to convert and then tagging that type with the JsonConverter to work around this?

like image 478
Ruben Bartelink Avatar asked Jun 28 '18 08:06

Ruben Bartelink


1 Answers

If I understood you correctly and according the official docs, you can apply converters directly and filter by type using a custom converter:

SomeType someObject = new SomeType();
string json = JsonConvert.SerializeObject(someObject, Formatting.Indented, new MyCustomConverter(typeof(SomeType)));

public class MyCustomConverter : JsonConverter
{

     private readonly Type[] _types;

     public MyCustomConverter (params Type[] types)
     {
         _types = types;
     }

     public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
         JToken t = JToken.FromObject(value);

         if (t.Type != JTokenType.Object)
         {
             t.WriteTo(writer);
         }
         else
         {
             JObject o = (JObject)t;
             IList<string> propertyNames = o.Properties().Select(p => p.Name).ToList();

             o.AddFirst(new JProperty("Keys", new JArray(propertyNames)));

             o.WriteTo(writer);
        }
     }

     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
     {
          throw new NotImplementedException();
     }

     public override bool CanRead
     {
          get { return false; }
     }

     public override bool CanConvert(Type objectType)
     {
          return _types.Any(t => t == objectType);
     }
}

Source: https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm

like image 103
Isma Avatar answered Oct 12 '22 15:10

Isma