将对象绑定到 Web API 端点时指定自定义属性名称
Posted
技术标签:
【中文标题】将对象绑定到 Web API 端点时指定自定义属性名称【英文标题】:Specifying custom property name when binding object to Web API endpoint 【发布时间】:2017-08-08 09:18:24 【问题描述】:我有一个 .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。
我该怎么做?
【问题讨论】:
【参考方案1】:TejSoft's answer 默认情况下在 ASP.NET Core 3.0 Web APIs 中不起作用。
从 3.0 开始,从 ASP.NET Core 共享框架中删除了 ASP.NET Core Json.NET (Newtonsoft.Json) 子组件。宣布“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;
【讨论】:
这是 .net core 3.0 的正确答案。 JsonProperty 对我们不起作用,很难弄清楚原因。进行此更改清除了一切。 我花了好几个小时才弄清楚... o.o【参考方案2】:更改您的包类并为您希望映射到不同 json 字段的每个字段添加 JsonProperty 装饰。
public class Package
[JsonProperty(PropertyName = "carrier")]
public string Carrier get; set;
[JsonProperty(PropertyName = "trackingNumber")]
public string TrackingNumber get; set;
【讨论】:
[JsonProperty(PropertyName = "tracking_Number")] 如果我想使用 XML 序列化会发生什么?【参考方案3】:我认为这也应该有效:
using Microsoft.AspNetCore.Mvc;
public class Package
[BindProperty(Name ="carrier")]
public string Carrier get; set;
[BindProperty(Name ="trackingNumber")]
public string TrackingNumber get; set;
【讨论】:
在 ASP.NET Core 中,这不适用于模型,但当您想要将“全局”属性(存在于多个/所有请求中)绑定到控制器本身。见Use BindProperty Attribute for Model Binding to Properties。【参考方案4】:通过使用自定义转换器,您将能够实现您所需要的。 以下基于属性的组件套件可能适合您的需求,并且非常通用,以防您想扩展它。
基础属性类
定义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"
【讨论】:
【参考方案5】:由于某种原因,以下在我的模型类中对我不起作用:
[JsonPropertyName("carrier")]
public string Carrier get; set;
虽然这有效:
[JsonProperty(PropertyName = "carrier")]
public string Carrier get; set;
一个来自 system.text.Json 库,另一个来自 Newtonsoft.json,但我真的很想知道为什么会这样。由于我试图在 API 调用中返回 Json 字符串中的属性值。
【讨论】:
我也遇到过这个问题。你找到原因了吗?在我的例子中,“[JsonPropertyName("carrier")]" 适用于一些属性,而对于其余属性,我必须在单个模型中使用 "[JsonProperty(PropertyName = "carrier")]"。【参考方案6】:如有要求:
[BindProperty(Name = "tracking_number")]
public string TrackingNumber get; set;
若有回应:
[JsonPropertyName("tracking_number")]
public string TrackingNumber get; set;
在这两种情况下:
[BindProperty(Name = "tracking_number")]
[JsonPropertyName("tracking_number")]
public string TrackingNumber get; set;
【讨论】:
【参考方案7】:在我的情况下,我不想更改属性名称 Carrier
和 TrackingNumber
所以我只是在 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"
【讨论】:
【参考方案8】:对于DotnetCore3.1我们可以使用
public class Package
[JsonProperty("carrier")]
public string Carrier get; set;
[JsonProperty("trackingNumber")]
public string TrackingNumber get; set;
【讨论】:
这个对我有用。【参考方案9】:如果你使用 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
【讨论】:
以上是关于将对象绑定到 Web API 端点时指定自定义属性名称的主要内容,如果未能解决你的问题,请参考以下文章
ASP Web API:将对象序列化为 JSON 时指定自定义字段名称
调用 Web API 2 端点时出现 HTTP 415 不支持的媒体类型错误
Ninja 框架端点在尝试将 JSON 映射到自定义对象时抛出 500 错误