Получение этого исключения «Newtonsoft. Json .JsonSerializationException» при попытке десериализации Json объекта - PullRequest
0 голосов
/ 10 февраля 2020

Как часть моего приложения Xamarin Forms, разработанного для Android, у меня есть файл конфигурации Json, в котором я сохраняю некоторые настройки, например, находится ли приложение в режиме отладки или что-то вроде пользователя, используемое для входа в систему. на веб-сайт.

Однако при попытке получить этот объект User из файла Json выбрасывается упомянутое исключение, которое также можно увидеть полностью ниже.

EXCEPTION   10-02-2020 14:06:08  Newtonsoft.Json.JsonSerializationException => Error converting value "{
  "$type": "Dental.App.Models.User, Dental.App",
  "username": "Ole",
  "password": "ole",
  "verifiedStatus": false,
  "creationTime": "10-02-2020 13:35:13"
}" to type 'Dental.App.Models.User'. Path 'User', line 5, position 197.; Stacktrace =>   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType (Newtonsoft.Json.JsonReader reader, System.Object value, System.Globalization.CultureInfo culture, Newtonsoft.Json.Serialization.JsonContract contract, System.Type targetType) [0x000bd] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x000d7] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue (Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.JsonConverter propertyConverter, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty, Newtonsoft.Json.JsonReader reader, System.Object target) [0x00061] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject (System.Object newObject, Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, System.String id) [0x00267] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x00154] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0006d] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000d9] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00053] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject (System.String value, System.Type type, Newtonsoft.Json.JsonSerializerSettings settings) [0x0002d] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject[T] (System.String value, Newtonsoft.Json.JsonSerializerSettings settings) [0x00000] in <f393073e86a643d9809b1a5d0c498495>:0 
  at Wolf.Utility.Main.Transport.JsonManipulator.ReadValueViaModel[T,U] (System.String path, System.String propertyName) [0x000c8] in <46eeb80ce9a440109e5bc07b0f1af244>:0 
  at Dental.App.Config.get_User () [0x00013] in <c94e9f8f60b14ec69bd9794bdf834717>:0 
  at Dental.App.Views.DentalWebPage.DentalWebView_LoadFinished (System.Object sender, System.EventArgs e) [0x00035] in <c94e9f8f60b14ec69bd9794bdf834717>:0 

User и ConfigModel объекты, которые я пытаюсь десериализовать, довольно просты. Их можно увидеть ниже вместе с разделом моего файла конфигурации, который содержит некоторые из моих свойств, хранящихся в Json, а затем мой Json.

public class User
    {
        [JsonProperty("username")]
        public string Username { get; set; }
        [JsonProperty("password")]
        public string Password { get; set; }
        [JsonProperty("verifiedStatus")]
        public bool VerifiedStatus { get; set; }
        [JsonProperty("creationTime")]
        public string CreationTime { get; private set; }

        [JsonIgnore]
        public DateTime TimeOfCreation => Convert.ToDateTime(CreationTime);

        public User()
        {
            CreationTime = DateTime.Now.ToString(CultureInfo.CurrentCulture);
        }

        [JsonConstructor]
        public User(string creationTime)
        {
            CreationTime = creationTime;
        }
    }
public class ConfigModel
    {
        public User User { get; set; }
    }
public class Config
    {
        private static User user = new User();
        public static User User
        {
            get => CanSave ? JsonManipulator.ReadValueViaModel<User, ConfigModel>(ConfigPath, nameof(User)) : user;
            set
            {
                if (CanSave) JsonManipulator.WriteValue(ConfigPath, nameof(User), value);
                else user = value;
            }
        }

        private static bool debugMode = true;
        public static bool DebugMode
        {
            get => CanSave ? JsonManipulator.ReadValue<bool>(ConfigPath, nameof(DebugMode)) : debugMode;
            set
            {
                if (CanSave) JsonManipulator.WriteValue(ConfigPath, nameof(DebugMode), value);
                else debugMode = value;
            }
        }       
...
}

{
  "DebugMode": "true",
  "UseCustomOptions": "false",
  "CustomFormats": "{\n  \"$type\": \"System.Collections.Generic.List`1[[ZXing.BarcodeFormat, zxing.portable]], mscorlib\",\n  \"$values\": []\n}",
  "User": "{\n  \"$type\": \"Dental.App.Models.User, Dental.App\",\n  \"username\": \"Ole\",\n  \"password\": \"ole\",\n  \"verifiedStatus\": false,\n  \"creationTime\": \"10-02-2020 13:35:13\"\n}",
  "SelectedMenu": "3"
}

Часть моего кода, которая выполняет всю работу по этому процессу, взята из моей служебной библиотеки. Эта библиотека является подмодулем на моем github, и полную ее можно найти здесь: https://github.com/andr9528/Wolf.Utility.Main

Ниже приведены методы из моего класса JsonManipulator, который выдает исключение, упомянутое при попытке десериализации в объект пользователя, т.е. T является пользователем. Для полного класса go по ссылке выше.

public class JsonManipulator
    {
        /// <summary>
        /// Parses Json file, and returns the attribute specified by 'propertyName'.
        /// </summary>
        /// <typeparam name="T">The type returned, from the property named after the input 'propertyName'.</typeparam>
        /// <param name="path">Path of the Json file.</param>
        /// <param name="propertyName">Name of the property to return.</param>
        /// <returns></returns>
        public static T ReadValue<T>(string path, string propertyName)
        {
            if (!File.Exists(path))
                throw new ArgumentNullException(nameof(path), $@"No file Exist on the specified path => {path}");

            if (path.Split('.').Last().ToLowerInvariant() != "json")
                throw new ArgumentException("The path given did not end in 'json'");

            var json = File.ReadAllText(path);

            try
            {
                var obj = JObject.Parse(json);
                if (obj != null)
                    return obj[propertyName].ToObject<T>();
                throw new OperationFailedException($"Failed to parse Json from the file located at => {path}");

            }
            catch (Exception ex)
            {
                throw;
            }
        }
        /// <summary>
        /// Deserializes Json file into specified model, and returns the property from it by the value specified in 'propertyName'.
        /// </summary>
        /// <typeparam name="T">The type returned, from the property named after the input 'propertyName'.</typeparam>
        /// <typeparam name="U">The model that is deserialized into, and from which the property is taken and returned.</typeparam>
        /// <param name="path">Path of the Json file.</param>
        /// <param name="propertyName">Name of the property to return.</param>
        /// <returns></returns>
        public static T ReadValueViaModel<T, U>(string path, string propertyName)
        {
            if (!File.Exists(path))
                throw new ArgumentNullException(nameof(path), $@"No file Exist on the specified path => {path}");

            if (path.Split('.').Last().ToLowerInvariant() != "json")
                throw new ArgumentException("The path given did not end in 'json'");

            var json = File.ReadAllText(path);

            try
            {
                var obj = JsonConvert.DeserializeObject<U>(json, new JsonSerializerSettings() 
                { 
                    NullValueHandling = NullValueHandling.Ignore, 
                    TypeNameHandling = TypeNameHandling.All
                });

                var prop = obj.GetType().GetProperties().First(x => x.Name == propertyName);

                return (T)prop.GetValue(obj);
            }
            catch (Exception)
            {

                throw;
            }
        }
...
}

Эта проблема связана только с получением части пользовательского свойства в моей конфигурации, и мне нужно вызвать ее, чтобы получить Пользовательский объект, содержащий имя пользователя и пароль.

Чтобы попытаться решить проблему, я внес ряд изменений, которые можно увидеть в приведенном выше коде. Вот некоторые примеры, если не все, следующие.

  1. Добавлены спецификации JsonProperty к свойствам объекта User

  2. Добавлен JsonConstructor для пользователя object

  3. Изменен тип свойства 'CreationTime' объекта User с DateTime на string.

  4. Создан метод ReadValueViaModel в JsonManipulator вместе с классом ConfigModel

Меня раздражает то, что я пропускаю лишь небольшой кусочек кода или форматирование где-то, но я просто не могу понять, где и что я скучаю по нему, чтобы он работал.

Не стесняйтесь задавать мне вопросы, чтобы очистить любую недостающую информацию.

РЕДАКТИРОВАТЬ 1: Обновлено Json форматирование - непосредственно скопировано из автоматически сгенерированного json файл. Согласно https://jsonlint.com/, это допустимо Json, поэтому мой метод WriteValue создает допустимый json.

1 Ответ

0 голосов
/ 11 февраля 2020

Может показаться, что вы делаете это, потому что вам неизвестны некоторые функции библиотеки. Это одна из наиболее часто используемых библиотек (в том смысле, что Microsoft включает ее по умолчанию в большинство типов веб-проектов) - пожалуйста, посмотрите на возможности библиотеки, прежде чем сходить с ума, пытаясь заново изобрести ее функции.

  • Если добавить к JSON новое поле, которого нет в вашей модели, оно игнорируется
  • Если в вашей модели отсутствует элемент, отсутствующий в JSON, он заполнен значением default(..); null для классов и значение по умолчанию для примитивов (например: 0 для чисел) Это не то, что JSON. net делает; поля просто игнорируются, то есть они содержат значения по умолчанию.
  • Если вы хотите добавить новое поле со значением по умолчанию, оно поддерживается.

Здесь предыдущий вопрос переполнения стека: Значение по умолчанию для отсутствующих свойств с JSON. net

Вот пример кода:

void Main()
{
    string testJSON = @"[{""FirstName"":""Michael"",""LastName"":""Jones""},{""FirstName"":""Jon"",""LastName"":""Smith""}]";
    PersonModel[] people = JsonConvert.DeserializeObject<PersonModel[]>(testJSON);
    people.Dump();
}

public class PersonModel
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public string GUID { get; set; }

    [DefaultValue(100)] //lets be generous when starting our loyalty program!
    [JsonProperty("LoyaltyPoints", DefaultValueHandling = DefaultValueHandling.Populate)]
    public int LoyaltyPoints { get; set; }
}

А вот снимок экрана дампа:

enter image description here


Обновление:

Вы еще не дали хорошая причина для того, чтобы делать то, что делаешь. Вместо хранения JSON объекта, который содержит строковый JSON объект, вы должны хранить что-то вроде этого:

{
    "DebugMode": "true",
    "UseCustomOptions": "false",
    "SelectedMenu": "3",
    "CustomFormats": [],
    "User": {
        "username": "Ole",
        "password": "ole",
        "verifiedStatus": "false",
        "creationTime": "10-02-2020 13:35:13"
    }
}

И ваши модели должны выглядеть так (очевидно, я предполагаю, что здесь немного как у вас не весь ваш код опубликован):

public class Config
{
    public bool DebugMode { get; set; }
    public bool UseCustomOptions { get; set; }
    public int SelectedMenu { get; set; }
    public List<ZXing.BarcodeFormat> CustomFormats { get; set; }
    public User User { get; set; }
}

public class User 
{
    public string Username { get; set; }
    public string Password { get; set; }
    public bool VerifiedStatus { get; set; }
    public DateTime CreationTime { get; set; }
}

void Main()
{
    //and then you can just ...
    string myJsonString = "...";
    Config config = JsonConvert.DeserializeObject<Config>(myJsonString);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...