Как часть моего приложения 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;
}
}
...
}
Эта проблема связана только с получением части пользовательского свойства в моей конфигурации, и мне нужно вызвать ее, чтобы получить Пользовательский объект, содержащий имя пользователя и пароль.
Чтобы попытаться решить проблему, я внес ряд изменений, которые можно увидеть в приведенном выше коде. Вот некоторые примеры, если не все, следующие.
Добавлены спецификации JsonProperty к свойствам объекта User
Добавлен JsonConstructor для пользователя object
Изменен тип свойства 'CreationTime' объекта User с DateTime на string.
Создан метод ReadValueViaModel в JsonManipulator вместе с классом ConfigModel
Меня раздражает то, что я пропускаю лишь небольшой кусочек кода или форматирование где-то, но я просто не могу понять, где и что я скучаю по нему, чтобы он работал.
Не стесняйтесь задавать мне вопросы, чтобы очистить любую недостающую информацию.
РЕДАКТИРОВАТЬ 1: Обновлено Json форматирование - непосредственно скопировано из автоматически сгенерированного json файл. Согласно https://jsonlint.com/, это допустимо Json, поэтому мой метод WriteValue создает допустимый json.