0

I want to have two test methods pass:

[TestMethod]
public void TestDeserializationSingleArray()
{
    // metaGrids is an array of arrays.
    var data = @"{
                    ""metaGrids"": [
                        [0, 0, 0],
                        [0, 0, 1],
                        [0, 0, 2]
                    ]
                }";
    var result = JsonConvert.DeserializeObject<Data>(data);
}

[TestMethod]
public void TestDeserializationMultipleArrays()
{
    // metaGrids is now an array of an array of arrays.
    var data = @"{
                    ""metaGrids"": [
                        [
                            [0, 0, 0],
                            [0, 0, 1],
                            [0, 0, 2]
                        ],
                        [
                            [0, 0, 0],
                            [0, 0, 1],
                            [0, 0, 2]
                        ]
                    ]
                }";
    var result = JsonConvert.DeserializeObject<Data>(data);
}

My object looks like this:

public class Data
{
    [JsonConverter(typeof(MetagridsDataConverter))]
    public int[][][] metaGrids;
}

I am trying to use a data converter class to make it work in both situations, but this is failing for me:

public class MetagridsDataConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {

    }
}

How can I code my converter so that it will work in both situations? In my above logic I keep getting this error (even though I'm just trying to baseline a case where I can get the converter to trigger properly):

Newtonsoft.Json.JsonSerializationException: Unexpected token when deserializing object: StartArray. Path 'metaGrids[0]', line 3, position 33.

Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • What you're trying is having either list of lists or list of lists of lists, right? https://stackoverflow.com/a/18997172/7034621 – orhtej2 Sep 13 '17 at 17:14

1 Answers1

1

Based on idea in this answer I came up with the following:

public class Data
{
    [JsonConverter(typeof(SingleOrArrayConverter))]
    public int[][][] metaGrids;
}

class SingleOrArrayConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<List<List<int>>>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Array)
        {

            switch (Dimensions(token))
            {
                case 3:
                    return token.ToObject<int[][][]>();
                case 2:
                    return new int[][][] { token.ToObject<int[][]>() };
            }
        }
        throw new InvalidOperationException("Data is not in a standard supported format.");
    }

    private static int Dimensions(JToken token)
    {
        if (token.Type != JTokenType.Array)
            return 0;
        return 1 + Dimensions(token.First);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}
Alexandru
  • 12,264
  • 17
  • 113
  • 208
orhtej2
  • 2,133
  • 3
  • 16
  • 26
  • This will still result in the object instance having a type of `Int32[][][]` for the `MetaGrids` property even though the data is of type `Int32[][]` – Aaron Roberts Sep 13 '17 at 17:38
  • @AaronRoberts Of course it will, it has to match the type of the entity being deserialized. – orhtej2 Sep 13 '17 at 17:52