JsonProperty WebApi request and response models

前端 未结 1 609
故里飘歌
故里飘歌 2020-12-21 23:02

I have an API that talks to another API. The response model looks something like this:

public class AddressResponseModel
{
    public string Id { get; set; }         


        
1条回答
  •  有刺的猬
    2020-12-21 23:28

    If you want to use the same classes to automatically generate JSON with different property names, without having to write a custom JsonConverter for each one, you're going to need to create your own custom ContractResolver, for instance:

    • Json.NET provides CamelCasePropertyNamesContractResolver.

    • This Answer has a prototype PascalCaseToUnderscoreContractResolver.

    If you have a deterministic way to map all .Net property names to the appropriate property names for a given usage context (post or get-from-api), you can create a contract resolver like one of the above.

    If, however, there's no general rule for mapping .Net property names for JSON property names, and each context may require some per-property customization, you could create your own ContractResolver that applies in a specific named context, and your own System.Attribute that supplies a JSON context name and property name to use in this context. I.e.:

    [System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
    public class JsonConditionalNameAttribute : System.Attribute
    {
        readonly string contextName;
        readonly string propertyName;
    
        public string ContextName { get { return contextName; } }
        public string PropertyName { get { return propertyName; } }
    
        public JsonConditionalNameAttribute(string contextName, string propertyName)
        {
            this.contextName = contextName;
            this.propertyName = propertyName;
        }
    }
    
    public class ConditionalNameContractResolver : DefaultContractResolver
    {
        readonly string contextName;
        public string ContextName { get { return contextName; } }
    
        public ConditionalNameContractResolver(string contextName)
            : base()
        {
            if (string.IsNullOrEmpty(contextName))
                throw new ArgumentNullException();
            if (string.IsNullOrEmpty(contextName))
                throw new ArgumentException();
            this.contextName = contextName;
        }
    
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            var jProperty = base.CreateProperty(member, memberSerialization);
            var attrs = jProperty.AttributeProvider.GetAttributes(typeof(JsonConditionalNameAttribute), true)
                .Cast()
                .Where(a => a.ContextName == ContextName)
                .Select(a => a.PropertyName)
                .Distinct()
                .ToList();
            if (attrs.Count == 1)
            {
                jProperty.PropertyName = attrs[0];
            }
            else if (attrs.Count > 1)
            {
                throw new JsonSerializationException(string.Format("Multiple conditional property attributes found for \"{0}\" in in context \"{1}\": \"{2}\"", jProperty, contextName, String.Join(",", attrs)));
            }
            return jProperty;
        }
    }
    

    Then your binding model would look something like:

    public static class AddressBindingModelContexts
    {
        public const string Post = "post";
        public const string GetFromApi = "getFromApi";
    }
    
    public class AddressBindingModel
    {
        [JsonConditionalName(AddressBindingModelContexts.GetFromApi, "address_1")]
        [JsonConditionalName(AddressBindingModelContexts.Post, "address1")]
        public string Address1 { get; set; }
    
        [JsonConditionalName(AddressBindingModelContexts.GetFromApi, "address_2")]
        [JsonConditionalName(AddressBindingModelContexts.Post, "address2")]
        public string Address2 { get; set; }
    
        [JsonProperty("city")]
        public string City { get; set; }
    
        [JsonProperty("county")]
        public string County { get; set; }
    
        [JsonProperty("postcode")]
        public string PostCode { get; set; }
    
        [JsonProperty("country")]
        public string Country { get; set; }
    
        [JsonConditionalName(AddressBindingModelContexts.GetFromApi, "save_as")]
        [JsonConditionalName(AddressBindingModelContexts.Post, "saveAs")]
        public string SaveAs { get; set; }
    }
    

    To test:

            var jsonFromApi = GetJsonFromApi();
    
            var postContract = new ConditionalNameContractResolver(AddressBindingModelContexts.Post);
            var getFromApiContract = new ConditionalNameContractResolver(AddressBindingModelContexts.GetFromApi);
    
            var model = JsonConvert.DeserializeObject(jsonFromApi, new JsonSerializerSettings { ContractResolver = getFromApiContract });
    
            var postJson = JsonConvert.SerializeObject(model, Formatting.Indented, new JsonSerializerSettings { ContractResolver = postContract });
    
            Debug.WriteLine(postJson); // Verify the postJson has the necessary properties and data.
    

    To change the contract resolver for all results returned from Web API, see JSON and XML Serialization in ASP.NET Web API: Camel Casing. To use a custom contract resolver when returning results from a specific Web Api call, see Customize Json result in Web API.

    0 讨论(0)
提交回复
热议问题