How to tell JSON.NET to deserialize JArray to a List<object> when object type is provided?

29,118

Solution 1

Your answer is probably here

Your [5] is an array. All you have to do is to cast it to a list.


You could also create your own converter, like described here

Solution 2

Currently, I have mocked the following solution:

public class MyObjectConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(object);
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        switch (reader.TokenType)
        {
            case Newtonsoft.Json.JsonToken.StartArray:
                return JToken.Load(reader).ToObject<List<object>>(); 
            case Newtonsoft.Json.JsonToken.StartObject:
                return JToken.Load(reader).ToObject<Dictionary<string, object>>(); 
            default:
                if (reader.ValueType == null && reader.TokenType != Newtonsoft.Json.JsonToken.Null)
                    throw new NotImplementedException("MyObjectConverter");
                return reader.Value;
        }
    }

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

    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException("MyObjectConverter");
    }
}

Problem of this solution is that it must handle the other types as well. It could fail when reader.TokenType is neither StartArray nor StartObject and reader.ValueType is null. Hopefully someone will provide a better solution.

Share:
29,118
Abimael López
Author by

Abimael López

Updated on July 01, 2020

Comments

  • Abimael López
    Abimael López almost 4 years

    Let's have the following class:

    class Foo
    {
        public object Any;
    }
    

    This class accepts anything in the field Any.

    When I call:

    JsonConvert.DeserializeObject<Foo>("{any: 5}")
    

    Any contains System.Int64.

    However, when I call:

    JsonConvert.DeserializeObject<Foo>("{any: [5]}")
    

    Any contains Newtonsoft.Json.Linq.JArray.

    How to configure JSON.NET so that in this case Any would contain List<object>?

    CLARIFICATION:

    There could be anything, I can call:

    JsonConvert.DeserializeObject<Foo>("{any: 'c'}")
    

    or

    JsonConvert.DeserializeObject<Foo>("{any: ['c', 5]}")
    

    More clarification:

    I would like to tell somehow to JSON.NET (maybe using JsonSerializerSettings):

    When you encounter object and JSON contains an array, deserialize that to (for instance) List<object>.

    • Andrew Whitaker
      Andrew Whitaker almost 9 years
    • Abimael López
      Abimael López almost 9 years
      @AndrewWhitaker see update
    • Daniel Möller
      Daniel Möller almost 9 years
      If Any is just 5 and not [5], would you like it to be a List<System.Int64> too?
    • Abimael López
      Abimael López almost 9 years
      @Daniel No, since there could be [5, 'x']. (Therefore, I have put object there.)
    • Chris
      Chris almost 9 years
      You can mark a constructor with JsonConstructorAttribute (newtonsoft.com/json/help/html/…) which will tell it to use that constructor when deserializing the object. You could then put any logic you want in there. Would that work for you?
    • Abimael López
      Abimael López almost 9 years
      @Chris I cannot annotate the particular classes, I need to specify that as a 'global' behavior. See "more clarification".
    • Chris
      Chris almost 9 years
      @TN.: I had a suspicion that might be the case. There are overloads of DeserializeObject that take JsonConverters that might do what you want but I've not used them. In general JsonConverters override read/write behaviour so should be able to do what you want but I can't tell you much more on how to use them. (Apologies if you've already checked if these overloads will help you) stackoverflow.com/questions/2254872/… may be of interest on this topic (may even be a duplicate)
    • Abimael López
      Abimael López almost 9 years
      @Chris I have already written few converters, however I do not know, how to specify that it should be invoked only when JSON contains an array (and type of member is object). Or how to fallback to the default behavior when JSON contains (for instance) a string.
  • Abimael López
    Abimael López almost 9 years
    I do not want to retraverse the structure and check and cast all fields with object type.
  • Abimael López
    Abimael López almost 9 years
    How to specify that a convert should be invoked only when JSON contains an array (and type of member is object)? Or how to fallback to the default behavior when JSON contains (for instance) a string?
  • Abimael López
    Abimael López almost 9 years
    Thank you for your code, but the Foo is just an example. I cannot hardwire the converter for a particular class (Foo) and property (Any).
  • Daniel Möller
    Daniel Möller almost 9 years
    Do you mean, you want that to work with any kind of object? Like Foo1, Foo2, Foo3 and any other?
  • Abimael López
    Abimael López almost 9 years
    @Daniel Yes, I have tried to mock the solution here: stackoverflow.com/a/31131934/121968.
  • Daniel Möller
    Daniel Möller almost 9 years
    You don't need to conver all types. You just need to convert the types you desire. If I understood it well, you just need to implement the Array case, any other case you just return the object as it is and JSON will do the rest. (It was able to manage those types without a converter, right?)
  • Abimael López
    Abimael López almost 9 years
    However, where I get 'the object' for the other cases?
  • Abimael López
    Abimael López almost 9 years
    ToObject<object> does not deserialize the nested arrays properly (e.g. [1, [5, 3]] or [1, {bar: [4]}]). The same problem has currently the solution above.
  • Daniel Möller
    Daniel Möller almost 9 years
    Do you get any exceptions if you just remove that if?
  • Abimael López
    Abimael López almost 9 years
    Maybe not, but want to know whether reader contains null in Value, because it should be there or not. But you can just ignore that if. Problem is that JToken.Load(reader).ToObject<List<object>>() does not process the nested array and maybe other JsonConvertors which are configured.