Похоже, что это ошибка с TypeNameHandling.Arrays
и многомерными массивами ранга> 2.
Я могу легче воспроизвести проблему, сериализовав трехмерный двойной массив, используя TypeNameHandling.Arrays
:
var root = new double[,,] { { { 1 } } };
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Arrays };
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
// Try to deserialize to the same type as root
// but get an exception instead:
var root2 = JsonConvert.DeserializeAnonymousType(json, root, settings);
JSON, сгенерированный с помощью приведенного выше кода:
{
"$type": "System.Double[,], mscorlib",
"$values": [ [ [ 1.0 ] ] ]
}
Наличие свойства "$type"
следует ожидать и задокументировано в Параметр TypeNameHandling , но, как вы заметили, он выглядит неправильно: у него должно быть дополнительное измерение в типе массива, например:
"$type": "System.Double[,,], mscorlib",
И на самом деле я могу успешно десериализовать JSON, еслиЯ вручную заменяю [,]
на [,,]
следующим образом:
// No exception!
JsonConvert.DeserializeAnonymousType(json.Replace("[,]", "[,,]"), root, settings)
Наконец, если я попробую тот же тест с 2d-массивом вместо 3d-массива, тест пройден.Демонстрационная скрипка здесь .
Причиной является ошибка в подпрограмме ReflectionUtils.RemoveAssemblyDetails
при вызове со следующей трассировкой:
Newtonsoft.Json.Utilities.ReflectionUtils.RemoveAssemblyDetails(string) C#
Newtonsoft.Json.Utilities.ReflectionUtils.GetTypeName(System.Type, Newtonsoft.Json.TypeNameAssemblyFormatHandling, Newtonsoft.Json.Serialization.ISerializationBinder) C#
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteTypeProperty(Newtonsoft.Json.JsonWriter, System.Type) C#
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteStartArray(Newtonsoft.Json.JsonWriter, object, Newtonsoft.Json.Serialization.JsonArrayContract, Newtonsoft.Json.Serialization.JsonProperty, Newtonsoft.Json.Serialization.JsonContainerContract, Newtonsoft.Json.Serialization.JsonProperty) C#
При вызове входной параметр имеет значение
System.Double[,,], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Но возвращается значение
System.Double[,], mscorlib
, что явно неверно.
При желании можно сообщить о проблеме в Newtonsoft здесь .
Обновление: Сегодня была открыта аналогичная проблема: Неверный тип многомерного массива # 1918 .
В качестве обходного пути вы должны ограничить область действия свойств, для которых вы выводите информацию о типе, ситуациями, когда данный объект JSON на практике может быть полиморфным.Возможны следующие варианты:
Вы можете сериализовать свой граф объектов с помощью TypeNameHandling.None
, но пометить свои полиморфные коллекции с помощью JsonPropertyAttribute.ItemTypeNameHandling = TypeNameHandling.Auto
примерно так:
public class Baz
{
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
public readonly List<Foo> Foos;
public Baz()
{
Foos = new List<Foo>();
}
}
Это решение обеспечивает менее раздутый JSON, а также минимизирует риски безопасности при использовании TypeNameHandling
, которые описаны в Предупреждение TypeNameHandling в Newtonsoft Json и Внешний jsonуязвим из-за Json.Net TypeNameHandling auto? и поэтому является предпочтительным решением.
Вы можете сериализовать свой граф объектов с помощью TypeNameHandling.None
и использовать пользовательский обработчик контрактов для установки JsonArrayContract.ItemTypeNameHandling
в TypeNameHandling.Auto
для коллекций с потенциально полиморфными элементами путем переопределения DefaultContractResolver.CreateArrayContract
.
Это будетРешение использовать, если вы не можете добавить атрибуты Json.NET к вашим типам.
Вы можете сериализовать свой граф объектов с помощью TypeNameHandling.Auto
или TypeNameHandling.Objects
.
Любой из вариантовпозволит избежать ошибки, а также уменьшить блот в вашем JSON, но не уменьшит ваши риски безопасности.
Вы можете сериализовать свой граф объектов с помощью JsonSerializerSettings.TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full
.
Это позволяет избежатьвызов RemoveAssemblyDetails()
, но приводит к еще более раздутому JSON и не позволяет избежать возможных угроз безопасности.