#c# #json
#c# #json
Вопрос:
Я пытаюсь создать какой-то атрибут, который я мог бы добавить в класс модели, чтобы во время десериализации json, если ключ имеет определенный формат (в данном случае заканчивается на _special
), он был добавлен в список в модели.
ie: C#
public class MyModel
{
public string Name { get; set; }
public IDictionary<string, string> SpecialFields { get; set; }
}
JSON
{
"Name": "John",
"Height_special": "72",
"Weight_special": "200"
}
Таким образом, результатом будет: MyModel.Name == "John"
и MyModel.SpecialFields
будут пары ключевых значений height и weight.
Возможно ли это?
РЕДАКТИРОВАТЬ: Проект использует .NET Core 3.1 и System.Text.Json
Комментарии:
1. Да, с помощью пользовательского конвертера JSON: newtonsoft.com/json/help/html/CustomJsonConverter.htm
2. Также, не могли бы вы предоставить немного больше деталей относительно того, где это будет использоваться? Похоже, что это веб-приложение или API; Я могу предположить, что это ASP.NET Ядро. Если да, то какая версия? Используете ли вы JSON.NET или новый
System.Text.Json
для десериализации? Из-за отсутствия этих деталей, вероятно, кто-то только что отклонил ваш пост и, возможно, что еще более важно, почему кому-то будет трудно вам помочь — нам нужно много работать наугад… Пожалуйста, проясните свой вопрос с немного более релевантными деталями . Спасибо!3. Спасибо за помощь. Добавил эти детали к вопросу
4. Для
System.Text.Json
этого ресурса лучше: thinktecture.com/en/asp-net /…
Ответ №1:
Вы можете сделать это с помощью пользовательского конвертера JSON для System.Text.Json
. Вот пример, который является расширяемым и подходит для вашего конкретного MyModel
класса:
public class MyModelJsonConverter : JsonConverter<MyModel>
{
public override MyModel Read(ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
MyModel model = new MyModel()
{
SpecialFields = new Dictionary<string, string>()
};
string nextPropertyName = string.Empty;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
nextPropertyName = reader.GetString();
break;
case JsonTokenType.String:
// Tempting to use reflection or switch here, but the if/else-if performs faster
if (nextPropertyName == "Name")
{
model.Name = reader.GetString();
continue;
}
// Make sure it's a _special field; if not, it's unrecognized!
int nameEndsAtIndex = nextPropertyName.IndexOf("_special");
if (nameEndsAtIndex == -1) throw new InvalidDataException("Unknown JSON field");
string key = nextPropertyName.Substring(0, nameEndsAtIndex);
string value = reader.GetString();
model.SpecialFields.Add(key, value);
break;
}
}
return model;
}
public override void Write(Utf8JsonWriter writer,
MyModel value,
JsonSerializerOptions options)
{
writer.WritePropertyName("Name");
writer.WriteStringValue(value.Name);
foreach (var specialField in value.SpecialFields)
{
writer.WritePropertyName($"{specialField.Key}_special");
writer.WriteStringValue(specialField.Value);
}
}
}
Это можно использовать следующим образом:
var options = new JsonSerializerOptions()
{
Converters = { new MyModelJsonConverter() }
};
var model = JsonSerializer.Deserialize<MyModel>(jsonString, options);
Обратите внимание, что этот пользовательский конвертер может выполнять обратное преобразование вашего MyModel
класса:
var jsonString = JsonSerializer.Serialize<MyModel>(model, options);
Для того, чтобы использовать эту настройку в ASP.NET Базовое приложение или веб-API, вам нужно добавить атрибут к вашему классу модели, чтобы позволить ASP.NET Core Framework знает, что вы собираетесь использовать пользовательский конвертер:
[JsonConverter(typeof(MyModelJsonConverter))]
public class MyModel
{
public string Name { get; set; }
public IDictionary<string, string> SpecialFields { get; set; }
}
… и, точно так же, преобразование будет обработано автоматически!
Я настоятельно рекомендую вам пошагово изучить методы Read
и Write
с точками останова, чтобы действительно понять, что происходит под ними. Эти методы будут вызываться JsonSerializer
напрямую; поняв, как они работают, вы сможете увидеть, как расширить этот пользовательский конвертер, если у вас возникнет такая необходимость.
Ответ №2:
Этого можно достичь с помощью нескольких шагов:
Создайте тип для полей Json:
public class SpecialClass
{
public string Name { get; set; }
public string Height_Special { get; set; }
public string Weight_Special { get; set; }
}
Преобразуйте Json с этим типом:
var myObject = JsonConvert.DeserializeObject<SpecialClass>(yourJson);
Создайте словарь _special полей как [высота как ключ, вес как значение]:
var dictionary = new Dictionary<string, string>()
{
{ myObject.Height_Special, myObject.Weight_Special }
};
Если требуется получить [ключ, значение] как [propertyName, значение]:
var dictionary = new Dictionary<string, string>()
{
{ nameof(myObject.Height_Special), myObject.Height_Special },
{ nameof(myObject.Weight_Special), myObject.Weight_Special }
};
Таким образом, вы можете получить требуемые данные в MyModel
виде:
MyModel myModel = new MyModel()
{
Name = myObject.Name,
SpecialFields = dictionary,
};
Комментарии:
1. Это действительно вручную — почему бы не использовать пользовательский конвертер, разработанный для этой цели ?
2. Кроме того, почему ваш
Dictionary<int, int>
? Вы имели в видуDictionary<string, string>
, как указано в OP в их сообщении? Почему вы отображаете ширину объекта на высоту ? OP хотел отобразить из имени свойства в значение свойства — не могли бы вы, пожалуйста, исправить свой пост, если планируете его сохранить?3. @CoolBots Поднятый вами вопрос не имеет большого значения для использования int вместо string. Пользователь может это понять. В любом случае, я обновил ответ. И если пользовательский конвертер не используется, это не значит, что это решение неправильное. Это тоже работает. И если решение в классической форме, то это не веская причина для его отклонения.
4. Я отклонил это, потому что это было неправильно; вы это исправили, поэтому я удалил отрицательный отзыв. Ваш подход «классической формы» (что бы это ни значило) в данном случае сродни залезанию в машину через окно — он работает…