1

I am looping over a folder of JSON files, and I am trying to pull out some information from them; however, I am finding it greatly difficult to do so.

I have started to build my objects to deserialize. I have a particular node I would normally deserialize as an object, but when it's empty, it presents as an empty array. Please see the definition field in the example JSON below:

{
  "name": "Example",
  "description": "Example JSON",
  "properties": {
    "foo": "bar",
    "foo1": "bar2",
    "foo3": "bar4"
  },
  "stages": {
    "This is a stage": {
      "stageInfo1": "blah",
      "stageInfo2": "blah",
      "integration": {
        "x": "x",
        "y": "y",
        "z": "z",
        "definition": []
      }
    },
    "Another Stage": {
      "stageInfo1": "blah",
      "stageInfo2": "blah",
      "integration": {
        "x": "x",
        "y": "y",
        "z": "z",
        "definition": {
          "5a4d7de4c6518": {
            "Editable": true,
            "ID": "5a4d7de4c6518",
            "Name": "My example"
          }
        }
      }
    }
  }
}

As the definition name can change (in this case it is 5a4d7de4c6518), I thought a dictionary would be best, but an error is thrown when an empty array is presented.

 [JsonProperty("definition")]
 public Dictionary<string, Definition> definition;

Error:

An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll

Additional information: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,JsonProcessReader.Models.Stages+IntegrationDefinition]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.

To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.

Community
  • 1
  • 1
Harambe
  • 997
  • 2
  • 8
  • 17
  • Possible duplicate of [Looping dynamic JSON to get all nodes C#](https://stackoverflow.com/questions/52721960/looping-dynamic-json-to-get-all-nodes-c-sharp) – Andrei Tătar Oct 10 '18 at 10:57
  • This error is generated because you said there will be JsonObject having key-value pair like "definition":{ } but when you have "definition":[] it's array not object, do you have any use case where "definition":[] is not empty? – Mihir Dave Oct 10 '18 at 11:06
  • @MihirDave, no, if the object is empty, it just offers an empty array. It's rather frustrating. – Harambe Oct 10 '18 at 11:16

1 Answers1

4

An empty array is incompatible with a dictionary structure, which gives rise to the error you are seeing. Since it seems you cannot easily change the JSON, you will need to use a JsonConverter to handle this situation. Here is a generic one that should work for you:

class TolerantObjectConverter<T> : JsonConverter where T: new()
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        object result = new T();
        if (token.Type == JTokenType.Object)
        {
            serializer.Populate(token.CreateReader(), result);
        }
        return result;
    }

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

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

This converter works by temporarily loading the relevant piece of JSON into a JToken and then checking whether it is actually an object before trying to convert it. So if it is an array or some other token type that won't convert properly, it will return an empty T instance instead.

To use the converter, simply add a [JsonConverter] attribute to your dictionary property like this:

public class Integration
{
    ...
    [JsonConverter(typeof(TolerantObjectConverter<Dictionary<string, Definition>>))]
    public Dictionary<string, Definition> definition { get; set; }
}

Here is a working demo: https://dotnetfiddle.net/83dQoC

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300