0

How do you convert a JObject to a 3D array? I have a JObject that is formatted as such:

{
  "0": [
    [
      1.0,
      2.0,
      3.0
    ],
    [
      4.0,
      5.0,
      6.0
    ]
  ],
  "1": [
    [
      7.0,
      8.0,
      9.0
    ],
    [
      10.0,
      11.0,
      12.0
    ]
  ]
}

I've tried casting it to a double[,,] but it fails with an error stating

Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'System.Double[,,]'.
0

2 Answers 2

1

The following works for me:

var deserailizationExp = JsonConvert.DeserializeObject<Dictionary<string, double[,]>>(@"
          {""0"": [
            [
              1.0,
              2.0,
              3.0
            ],
            [
              4.0,
              5.0,
              6.0
            ]
          ],
          ""1"": [
            [
              7.0,
              8.0,
              9.0
            ],
            [
              10.0,
              11.0,
              12.0
            ]
          ]
        }");

You could then either use the dictionary directly or transform it to an array. Edit: As pointed out in the comments on this, you could also consider deserializing this to the type SortedDictionary<int, double[,]>. I tested that as a type and it worked for me.

Alternatively, if you modify the JSON you can do the following:

var otherDes = JsonConvert.DeserializeObject<double[,,]>(@"
          [[
            [
              1.0,
              2.0,
              3.0
            ],
            [
              4.0,
              5.0,
              6.0
            ]
          ],
          [
            [
              7.0,
              8.0,
              9.0
            ],
            [
              10.0,
              11.0,
              12.0
            ]
          ]
        ]");

As you can see, I just removed "0" and "1" and replaced {} with []. If you have the ability to control how you're receiving the JSON somehow this would probably be the better solution in my opinion since it matches your requested type without having to do any further operations on it.

Sign up to request clarification or add additional context in comments.

2 Comments

Dictionary<int, double[,]> should also work, since the property names are clearly indices. Watch out for the fact that Dictionary is unordered so the order of the entries may get scrambled during deserialization. SortedDictionary<int, double[,]> would be better.
but this won't work if he is receiving content from a web service
0

Json.NET expects a multidimensional array to be formatted like a 3d jagged array in the JSON file, however yours is formatted like a dictionary of 2d jagged arrays. You can use a custom JsonConverter to convert JSON in such a format to a 3d array, like so:

public class Array3DConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        if (!objectType.IsArray)
            return false;
        return objectType.GetArrayRank() == 3;
    }

    object ReadJsonGeneric<T>(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        if (reader.TokenType == JsonToken.StartArray)
        {
            // Handle case when it's actually a 3d array in the JSON.
            var list = serializer.Deserialize<List<List<List<T>>>>(reader);
            return list.Select((l, i) => new KeyValuePair<int, List<List<T>>>(i, l)).To3DArray();
        }
        else if (reader.TokenType == JsonToken.StartObject)
        {
            // Handle case when it's a dictionary of key/value pairs.
            var dictionary = serializer.Deserialize<SortedDictionary<int, List<List<T>>>>(reader);
            return dictionary.To3DArray();
        }
        else
        {
            throw new JsonSerializationException("Invalid reader.TokenType " + reader.TokenType);
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        try
        {
            var elementType = objectType.GetElementType();
            var method = GetType().GetMethod("ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            return method.MakeGenericMethod(new[] { elementType }).Invoke(this, new object[] { reader, objectType, existingValue, serializer });
        }
        catch (TargetInvocationException ex)
        {
            // Wrap the TargetInvocationException in a JsonSerializerException
            throw new JsonSerializationException("Failed to deserialize " + objectType, ex);
        }
    }

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

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

public static class EnumerableExtensions
{
    public static T[,,] To3DArray<T>(this IEnumerable<KeyValuePair<int, List<List<T>>>> jaggedArray)
    {
        if (jaggedArray == null)
            throw new ArgumentNullException("jaggedArray");
        var counts = new int[3];
        foreach (var pair in jaggedArray)
        {
            var i = pair.Key;
            counts[0] = Math.Max(i + 1, counts[0]);
            if (pair.Value == null)
                continue;
            var jCount = pair.Value.Count;
            counts[1] = Math.Max(jCount, counts[1]);
            for (int j = 0; j < jCount; j++)
            {
                if (pair.Value[j] == null)
                    continue;
                var kCount = pair.Value[j].Count;
                counts[2] = Math.Max(kCount, counts[2]);
            }
        }
        var array = new T[counts[0], counts[1], counts[2]];
        foreach (var pair in jaggedArray)
        {
            var i = pair.Key;
            if (pair.Value == null)
                continue;
            var jCount = pair.Value.Count;
            for (int j = 0; j < jCount; j++)
            {
                if (pair.Value[j] == null)
                    continue;
                var kCount = pair.Value[j].Count;
                for (int k = 0; k < kCount; k++)
                    array[i, j, k] = pair.Value[j][k];
            }
        }
        return array;
    }
}

Then use it like:

var array = JsonConvert.DeserializeObject<double[, ,]>(jsonString, new JsonSerializerSettings { Converters = new[] { new Array3DConverter() } });

Or, if you have already parsed your JSON string into a JObject, you can use JToken.ToObject<T>(JsonSerializer) to deserialize to your desired type using your converter:

var array = jObj.ToObject<double[, ,]>(JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = new[] { new Array3DConverter() } }));

For flexibility in use, the converter tests to see whether the incoming JSON is formatted as an object or array and responds appropriately.

Note - only lightly tested.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.