Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
943 views
in Technique[技术] by (71.8m points)

json - JsonProperty WebApi request and response models

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

public class AddressResponseModel
{
    public string Id { get; set; }
    public string SaveAs { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string County { get; set; }
    public string Country { get; set; }
    public string PostCode { get; set; }
}

So, I need to send this to another API. I don't really want to play around with the response in JavaScript, I would just like to send it as it is to my endpoint and let the server handle its factorization. So, I tried to do this:

public class AddressBindingModel
{
    [Required]
    [JsonProperty("address_1")]
    public string Address1 { get; set; }

    [JsonProperty("address_2")]
    public string Address2 { get; set; }

    [Required]
    [JsonProperty("city")]
    public string City { get; set; }

    [Required]
    [JsonProperty("county")]
    public string County { get; set; }

    [Required]
    [JsonProperty("postcode")]
    public string PostCode { get; set; }

    [Required]
    [JsonProperty("country")]
    public string Country { get; set; }

    [JsonProperty("save_as")]
    public string SaveAs { get; set; }
}

but the problem with that is that it expects the json to follow to same property format. How can I get it to expect my unmodified response model, but output the JSON with the underscores?

To clarify, I will post my model like this:

{
    address1: '123',
    address2: 'Some street',
    city: 'London',
    county: 'London',
    country: 'GB',
    saveAs: 'Home'
}

and my API will then send this to another API like this:

{
    address_1: '123',
    address_2: 'Some street',
    city: 'London',
    county: 'London',
    country: 'GB',
    save_as: 'Home'
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

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<JsonConditionalNameAttribute>()
            .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<AddressBindingModel>(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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...