Указание имени настраиваемого свойства при привязке объекта к конечной точке веб-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; }
}

Надеюсь, это поможет!