【问题标题】:Specifying custom property name when binding object to Web API endpoint将对象绑定到 Web API 端点时指定自定义属性名称
【发布时间】: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。

我该怎么做?

【问题讨论】:

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


    【解决方案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; }
    }
    

    【讨论】:

    【解决方案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】:

        在我的情况下,我不想更改属性名称 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"
        }
        

        【讨论】:

          【解决方案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

          【讨论】:

            猜你喜欢
            • 2016-04-02
            • 2018-12-07
            • 2020-11-09
            • 1970-01-01
            • 2011-04-17
            • 1970-01-01
            • 2016-04-04
            • 1970-01-01
            • 2013-10-09
            相关资源
            最近更新 更多