【问题标题】:Convert JObject to multi-dimensional array将 JObject 转换为多维数组
【发布时间】:2016-09-19 20:07:18
【问题描述】:

如何将 JObject 转换为 3D 数组?我有一个格式如下的 JObject:

{
  "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
    ]
  ]
}

我尝试将其转换为 double[,,],但失败并显示错误提示

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

【问题讨论】:

    标签: c# arrays json casting json.net


    【解决方案1】:

    以下对我有用:

    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
                ]
              ]
            }");
    

    然后您可以直接使用字典或将其转换为数组。 编辑:正如 cmets 中所指出的,您还可以考虑将其反序列化为 SortedDictionary&lt;int, double[,]&gt; 类型。我将它作为一种类型进行了测试,它对我有用。

    或者,如果您修改 JSON,您可以执行以下操作:

    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
                ]
              ]
            ]");
    

    如您所见,我只是删除了“0”和“1”,并将 {} 替换为 []。如果您能够以某种方式控制接收 JSON 的方式,我认为这可能是更好的解决方案,因为它与您请求的类型匹配,而无需对其进行任何进一步的操作。

    【讨论】:

    • Dictionary&lt;int, double[,]&gt; 也应该可以工作,因为属性名称显然是索引。注意Dictionary 是无序的,因此在反序列化过程中条目的顺序可能会被打乱。 SortedDictionary&lt;int, double[,]&gt; 会更好。
    • 但如果他从网络服务接收内容,这将不起作用
    【解决方案2】:

    Json.NET 期望多维数组的格式类似于 JSON 文件中的 3d 锯齿状数组,而您的格式则类似于 2d 锯齿状数组的字典。您可以使用custom JsonConverter 将这种格式的 JSON 转换为 3d 数组,如下所示:

    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;
        }
    }
    

    然后像这样使用它:

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

    或者,如果您已经将 JSON 字符串解析为 JObject,则可以使用 JToken.ToObject&lt;T&gt;(JsonSerializer) 使用转换器反序列化为所需的类型:

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

    为了使用的灵活性,转换器会测试传入的 JSON 是否被格式化为对象或数组并做出适当的响应。

    注意 - 仅经过轻微测试。

    【讨论】:

      猜你喜欢
      • 2023-03-26
      • 2019-06-29
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多