Указание имени настраиваемого свойства при привязке объекта к конечной точке веб-API
У меня есть .Net Core Web API. Он автоматически отображает модели, когда свойства модели соответствуют телу запроса. Например, если у вас есть этот класс:
public class Package
{
public string Carrier { get; set; }
public string TrackingNumber { get; set; }
}
Он правильно привяжет его к конечной точке POST, если тело запроса является следующим JSON:
{
carrier: "fedex",
trackingNumber: "123123123"
}
Что мне нужно сделать, это указать настраиваемое свойство для сопоставления. Например, используя тот же класс выше, мне нужно иметь возможность сопоставлять JSON, если TrackingNumber входит как tracking_number
.
Как это сделать?
Ответы
Ответ 1
Измените класс пакета и добавьте декодер JsonProperty для каждого поля, которое вы хотите сопоставить с другим полем json.
public class Package
{
[JsonProperty(PropertyName = "carrier")]
public string Carrier { get; set; }
[JsonProperty(PropertyName = "trackingNumber")]
public string TrackingNumber { get; set; }
}
Ответ 2
Я думаю, что это должно работать тоже:
using Microsoft.AspNetCore.Mvc;
public class Package
{
[BindProperty(Name ="carrier")]
public string Carrier { get; set; }
[BindProperty(Name ="trackingNumber")]
public string TrackingNumber { get; set; }
}
Ответ 3
Используя пользовательский конвертер, вы сможете достичь того, что вам нужно.
Следующий набор компонентов на основе атрибутов может удовлетворить ваши потребности и является довольно общим, если вы хотите его расширить.
Класс базового атрибута
Определяет IsMatch
, который позволяет определить, соответствует ли свойство объекта свойству json.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public abstract class JsonDeserializationPropertyMatchAttribute : Attribute
{
protected JsonDeserializationPropertyMatchAttribute() { }
public abstract bool IsMatch(JProperty jsonProperty);
}
Пример реализации: имена нескольких десериализации
Определяет атрибут, который позволяет вам иметь несколько имен, связанных с свойством. Реализация IsMatch
просто проходит через них и пытается найти совпадение.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class JsonDeserializationNameAttribute : JsonDeserializationPropertyNameMatchAttribute
{
public string[] PropertyNames { get; private set; }
public JsonDeserializationNameAttribute(params string[] propertyNames)
{
this.PropertyNames = propertyNames;
}
public override bool IsMatch(JProperty jsonProperty)
{
return PropertyNames.Any(x => String.Equals(x, jsonProperty.Name, StringComparison.InvariantCultureIgnoreCase));
}
}
Конвертер, чтобы привязать оба атрибута к десериализации json, требуется следующий конвертер:
public class JsonDeserializationPropertyMatchConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsClass;
}
public override bool CanWrite
{
get
{
return false;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var constructor = objectType.GetConstructor(new Type[0]);
if (constructor == null)
throw new JsonSerializationException("A parameterless constructor is expected.");
var value = constructor.Invoke(null);
var jsonObject = JObject.Load(reader);
var jsonObjectProperties = jsonObject.Properties();
PropertyInfo[] typeProperties = objectType.GetProperties();
var typePropertyTuples = new List<Tuple<PropertyInfo, Func<JProperty, bool>>>();
foreach (var property in typeProperties.Where(x => x.CanWrite))
{
var attribute = property.GetCustomAttribute<JsonDeserializationPropertyMatchAttribute>(true);
if (attribute != null)
typePropertyTuples.Add(new Tuple<PropertyInfo, Func<JProperty, bool>>(property, attribute.IsMatch));
else
typePropertyTuples.Add(new Tuple<PropertyInfo, Func<JProperty, bool>>(property, (x) => false));
}
foreach (JProperty jsonProperty in jsonObject.Properties())
{
var propertyTuple = typePropertyTuples.FirstOrDefault(x => String.Equals(jsonProperty.Name, x.Item1.Name, StringComparison.InvariantCultureIgnoreCase) || x.Item2(jsonProperty));
if (propertyTuple != null)
propertyTuple.Item1.SetValue(value, jsonProperty.Value.ToObject(propertyTuple.Item1.PropertyType, serializer));
}
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Пример, используя коды, вставленные выше, и, украсив класс следующим образом, мне удалось получить десериализацию объектов:
[JsonConverter(typeof(JsonDeserializationPropertyMatchConverter))]
public class Package
{
public string Carrier { get; set; }
[JsonDeserializationName("Tracking_Number","anotherName")]
public string TrackingNumber { get; set; }
}
Выход 1
var input = "{ carrier: \"fedex\", trackingNumber: \"123123123\" }";
var output = JsonConvert.DeserializeObject<Package>(input); // output.TrackingNumber is "123123123"
Выход 2
var input = "{ carrier: \"fedex\", tracking_Number: \"123123123\" }";
var output = JsonConvert.DeserializeObject<Package>(input); // output.TrackingNumber is "123123123"
Выход 3
var input = "{ carrier: \"fedex\", anotherName: \"123123123\" }";
var output = JsonConvert.DeserializeObject<Package>(input); // output.TrackingNumber is "123123123"
Ответ 4
Ответ TejSoft по умолчанию не работает в веб-API ASP.NET Core 3.0.
Начиная с версии 3.0, подкомпонент ASP.NET Core Json.NET(Newtonsoft.Json) удаляется из общей платформы ASP.NET Core. Объявлено, что "Json.NET продолжит работать с ASP.NET Core, но не будет работать с общей инфраструктурой". Недавно добавленный Json Api утверждал, что он специально предназначен для высокопроизводительных сценариев.
Используйте атрибут JsonPropertyName
для установки имени пользовательского свойства:
using System.Text.Json.Serialization;
public class Package
{
[JsonPropertyName("carrier")]
public string Carrier { get; set; }
[JsonPropertyName("tracking_number")]
public string TrackingNumber { get; set; }
}
Надеюсь, это поможет!