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

1

8

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?

Gewirtz answered 28/6, 2018 at 8:0 Comment(0)
M
1

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

Maurey answered 11/9, 2018 at 11:49 Comment(2)
Hey thanks for taking the time to respond and sorry for the slow response - the issue with this is that if I am serializing an A containing a B that needs field C to have converter D applied to it, I can't just pass D - I'd need to walk the structure to make sure I don't need converter E too and would not be any better off than putting them in the settings. So, to restate the problem in terms of your answer, the puzzle is how do I get MyCustomConverter to work on D without being passed in externally. I am ok with putting an Attribute on B or C. Not OK to have to pass in code or put on A.Gewirtz
IOW I have the converter, I have a nested structure which contains something that the converter works on if I pass it to JsonConvert or in the Settings. If I put it on the innermost type which is exactly what's needed, it sometimes works. But I if it's a Tuple<T, U> I cant put anything on it to make U be converted.Gewirtz

© 2022 - 2024 — McMap. All rights reserved.