JSON.net: как десериализовать без использования конструктора по умолчанию?
У меня есть class, который имеет конструктор по умолчанию, а также перегруженный конструктор, который принимает набор параметров. Эти параметры соответствуют полям объекта и назначаются при построении. На данный момент мне нужен конструктор по умолчанию для других целей, поэтому я хотел бы сохранить его, если смогу.
Моя проблема: если я удаляю конструктор по умолчанию и передаю строку JSON, объект десериализуется правильно и передает параметры конструктора без каких-либо проблем. Я в конечном итоге возвращаю объект, заполненный так, как я ожидал. Однако, как только я добавлю конструктор по умолчанию в объект, когда я вызываю JsonConvert.DeserializeObject(jsontext)
свойства больше не заполняются.
На этом этапе я попытался добавить new JsonSerializerSettings(){CheckAdditionalContent = true}
к вызову десериализации. что ничего не делало.
- Дезаминирование гетерогенного массива JSON в ковариантный список с использованием JSON.NET
- Сериализация нулевого значения в JSON.NET
- JSON.NET: зачем использовать JToken - когда-либо?
- Как настроить пользовательские настройки JsonSerializer для Json.NET в веб-API MVC 4?
- Преобразовать Newtonsoft.Json.Linq.JArray в список определенного типа объекта
Еще одно замечание. параметры конструктора точно соответствуют именам полей, за исключением того, что параметры начинаются с строчной буквы. Я бы не подумал, что это будет иметь значение, поскольку, как я уже упоминал, десериализация отлично работает без конструктора по умолчанию.
Вот пример моих конструкторов:
public Result() { } public Result(int? code, string format, Dictionary details = null) { Code = code ?? ERROR_CODE; Format = format; if (details == null) Details = new Dictionary(); else Details = details; }
- Создайте строго типизированный объект c # из объекта json с ID как имя
- Как конвертировать JSON в XML или XML в JSON?
- JToken: получить исходное / оригинальное значение JSON
- как десериализовать JSON в IEnumerable с помощью Newtonsoft JSON.NET
- Заставить JSON.NET включать миллисекунды при сериализации DateTime (даже если компонент ms равен нулю)
- Конвенция о присвоении имен JSON
- Сериализовать контейнер перечислений в виде строк с помощью JSON.net
- Не удалось загрузить файл или сборку «Newtonsoft.Json» или одну из его зависимостей. Определение манифеста не соответствует ссылке на сборку
Json.Net предпочитает использовать конструктор по умолчанию (без параметров) для объекта, если он есть. Если есть несколько конструкторов, и вы хотите, чтобы Json.Net использовал нестандартный, вы можете добавить [JsonConstructor]
к конструктору, который вы хотите вызвать Json.Net.
[JsonConstructor] public Result(int? code, string format, Dictionary details = null) { ... }
Важно, чтобы имена параметров конструктора соответствовали соответствующим именам свойств объекта JSON (игнорируя случай), чтобы это работало правильно. Однако необязательно иметь параметр конструктора для каждого свойства объекта. Для тех свойств объекта JSON, которые не охвачены параметрами конструктора, Json.Net будет пытаться использовать общедоступные объекты доступа (или свойства / поля, помеченные [JsonProperty]
), чтобы заполнить объект после его создания.
Если вы не хотите добавлять атрибуты в свой class или иначе не управляете исходным кодом для classа, который вы пытаетесь десериализовать, то другой альтернативой является создание настраиваемого JsonConverter для создания и заполнения вашего объекта. Например:
class ResultConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(Result)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // Load the JSON for the Result into a JObject JObject jo = JObject.Load(reader); // Read the properties which will be used as constructor parameters int? code = (int?)jo["Code"]; string format = (string)jo["Format"]; // Construct the Result object using the non-default constructor Result result = new Result(code, format); // (If anything else needs to be populated on the result object, do that here) // Return the result return result; } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
Затем добавьте конвертер в настройки вашего сериализатора и используйте настройки при десериализации:
JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Converters.Add(new ResultConverter()); Result result = JsonConvert.DeserializeObject(jsontext, settings);
Немного поздно и здесь не совсем подходит, но я собираюсь добавить свое решение здесь, потому что мой вопрос был закрыт как дубликат этого, и потому что это решение совершенно другое.
Мне нужен был общий способ проинструктировать Json.NET
чтобы предпочесть конкретный конструктор для определенного типа структуры, поэтому я могу опустить атрибуты JsonConstructor
которые добавили бы зависимость от проекта, где каждая такая структура определена.
Я немного изменил конструкцию и внедрил пользовательский разрешитель контрактов, где я переопределил метод CreateObjectContract
чтобы добавить свою логику пользовательского создания.
public class CustomContractResolver : DefaultContractResolver { protected override JsonObjectContract CreateObjectContract(Type objectType) { var c = base.CreateObjectContract(objectType); if (!IsCustomStruct(objectType)) return c; IList list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList(); var mostSpecific = list.LastOrDefault(); if (mostSpecific != null) { c.OverrideCreator = CreateParameterizedConstructor(mostSpecific); c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties)); } return c; } protected virtual bool IsCustomStruct(Type objectType) { return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System."); } private ObjectConstructor
Я использую его вот так.
public struct Test { public readonly int A; public readonly string B; public Test(int a, string b) { A = a; B = b; } } var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings { ContractResolver = new CustomContractResolver() }); var t = JsonConvert.DeserializeObject(json); tAShouldEqual(1); tBShouldEqual("Test");