0

I'm currently having set of apis and implementing v2 of certain APIs. Controller level I made the URL as /v2/. I have a requirement where one of the API response is

Http Response

{
 "prd":"product name"
}

Product.cs

public class ProductInfo
{
  [JsonProperty("prd")]
  public string ProductName { get; set;}
}

In the version2 of API I want to change the JsonProperty("prd") to JsonProperty("product_name"). How to dynamically change the property name and Ignore certain property based on the version of the API. I don't want to duplicate the service layer

Ganesh
  • 13
  • 4
  • Not sure this is a good approach, you should probably duplicate your class for each version of the API. – LoïcR Apr 05 '19 at 07:34
  • 3
    You should consider using DTOs instead of exposing your domain models through the API. That way you could easily map ProductInfo to DTOs for your v1/v2 endpoints. I recommend reading [this answer](https://stackoverflow.com/a/36175349/2358221) as to why this good practice. – Boxiom Apr 05 '19 at 07:35
  • You better duplicate your DTOs rather than trying to change dynamically the property name, maintenance-wise. – Spotted Apr 05 '19 at 08:24
  • I would prefer to use ApiVersion attribute and make sure that each version has its own specific contract. And caller should specify version number either in URL or header or query string. So that we know exactly which schema should be expected. Makes solution more typesafe. – Manoj Choudhari Apr 05 '19 at 08:49
  • Tried a few different techniques and none ever seem to work exactly, created a repo to see if extern aliases would be useful https://github.com/whatisthejava/versioning-with-external-aliases – whatisthejava Aug 19 '19 at 15:34

1 Answers1

-2

You can achieve this using custom ContractResolver in Json.Net. Here, I have created a simple example of the demonstration. You can execute it based on your API versioning condition.

void Main()
{
    var productInfo = new ProductInfo();
    productInfo.ProductName = "Product Name";

    var jsonResolver = new PropertyRenameSerializerContractResolver();
    jsonResolver.RenameProperty(typeof(ProductInfo), "prd", "product_name");

    var serializerSettings = new JsonSerializerSettings();
    serializerSettings.ContractResolver = jsonResolver;

    var json = JsonConvert.SerializeObject(productInfo, serializerSettings);

    Console.WriteLine(json);
}

public class ProductInfo
{
    [JsonProperty("prd")]
    public string ProductName { get; set; }
}

public class PropertyRenameSerializerContractResolver : DefaultContractResolver
{
    private readonly Dictionary<Type, Dictionary<string, string>> _renames;

    public PropertyRenameSerializerContractResolver()
    {
        _renames = new Dictionary<Type, Dictionary<string, string>>();
    }

    public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
    {
        if (!_renames.ContainsKey(type))
            _renames[type] = new Dictionary<string, string>();

        _renames[type][propertyName] = newJsonPropertyName;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
            property.PropertyName = newJsonPropertyName;

        return property;
    }

    private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
    {
        Dictionary<string, string> renames;

        if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
        {
            newJsonPropertyName = null;
            return false;
        }

        return true;
    }
}

The output of this program will look like this:

{"product_name":"Product Name"}

I had used this in one of the scenarios which match with your scenario. For more details, you can refer to the following article.

Advanced Newtonsoft.Json: Dynamically rename or ignore properties without changing the serialized class

Keyur Ramoliya
  • 1,900
  • 2
  • 16
  • 17
  • Downvoting because I think this is overkill and complexifies OP's code more than necessary. – Spotted Apr 05 '19 at 08:27
  • @Spotted I humbly accepting your downvote but I think you had considered this as one of the ways to achieve this. Apart from that, do you think that all the people are answering the question by considering OP's code? Sometimes we need something that should work. – Keyur Ramoliya Apr 05 '19 at 08:52
  • Of course I considered your answer as one way to achieve this (that's the reason you posted it, no ?). I think too few people consider OPs' codes before answering. And yes, in the end one need something that works. In this case, see @Boxiom's comment. – Spotted Apr 05 '19 at 09:00
  • @KeyurRamoliya I referred the same site and used the ContractResolver and successfully renamed the properties. Thanks – Ganesh Apr 05 '19 at 10:38
  • 1
    this is one of those situations where just because you can do something, you should definitely not actually do it. – Andrei Dragotoniu Apr 08 '19 at 11:57