将对象绑定到Web API端点时指定自定义属性名称

时间:2017-03-17 00:06:27

标签: c# asp.net-web-api asp.net-core

我有一个.Net Core Web API。当模型属性与请求主体匹配时,它会自动映射模型。例如,如果您有这个类:

public class Package
{
    public string Carrier { get; set; }
    public string TrackingNumber { get; set; }
}

如果请求主体是以下JSON,它会正确地将它绑定到POST端点:

{
    carrier: "fedex",
    trackingNumber: "123123123"
}

我需要做的是指定要映射的自定义属性。例如,使用上面的同一个类,如果TrackingNumber以tracking_number的形式出现,我需要能够映射到JSON。

我该怎么做?

8 个答案:

答案 0 :(得分:14)

更改您的包类并为您希望映射到不同json字段的每个字段添加JsonProperty装饰。

public class Package
{
    [JsonProperty(PropertyName = "carrier")]
    public string Carrier { get; set; }

    [JsonProperty(PropertyName = "trackingNumber")]
    public string TrackingNumber { get; set; }
}

答案 1 :(得分:7)

默认情况下, TejSoft的答案在 ASP.NET Core 3.0 Web API 中不起作用。

从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; }
}

希望有帮助!

答案 2 :(得分:6)

我认为这也应该起作用:

using Microsoft.AspNetCore.Mvc;
public class Package
{
     [BindProperty(Name ="carrier")]
     public string Carrier { get; set; }

     [BindProperty(Name ="trackingNumber")]
     public string TrackingNumber { get; set; }
}

答案 3 :(得分:0)

通过使用自定义转换器,您将能够实现所需 基于属性的以下组件套件可能适合您的需求,并且在您想要扩展它时非常通用。

基本属性类

定义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 :(得分:0)

就我而言,我不想更改属性名称CarrierTrackingNumber

所以我只需在JsonResult响应上添加new JsonSerializerSettings()

public JsonResult GetJQXGridData(){
    var Data = .......
    return Json(Data, new JsonSerializerSettings()) //change here
}

不使用JsonSerializerSettings输出

{
    carrier: "fedex",
    trackingNumber: "123123123"
}

使用JsonSerializerSettings输出

{
    Carrier: "fedex",
    TrackingNumber: "123123123"
}

答案 5 :(得分:0)

对于DotnetCore3.1,我们可以使用

public class Package
{

    [JsonProperty("carrier")]
    public string Carrier { get; set; }

    [JsonProperty("trackingNumber")]
    public string TrackingNumber { get; set; }
}

答案 6 :(得分:0)

如果您使用XML序列化(ContentTypes.TextXml),则可以使用属性 [XmlElement(ElementName = "name")]更改字段名称。通过使用命名空间System.Xml.Serialization。

我尝试了[JsonProperty("name")][System.Runtime.Serialization.DataMember(Name = "name")],但对于我的XML内容,它们不起作用。

有关更多信息,请阅读https://docs.microsoft.com/en-us/dotnet/standard/serialization/controlling-xml-serialization-using-attributes

答案 7 :(得分:0)

出于某种原因,以下在我的模型类中对我不起作用:

[JsonPropertyName("carrier")]
 public string Carrier { get; set; }

虽然这有效:

[JsonProperty(PropertyName = "carrier")]
public string Carrier { get; set; }

一个来自 system.text.Json 库,另一个来自 Newtonsoft.json 但我真的很想知道为什么会这样。因为我试图在 API 调用中返回 Json 字符串中的属性值。