Home > other >  deserialize response and store in different object when the response property value are different
deserialize response and store in different object when the response property value are different

Time:03-26

I wanted to deserialized an API response and extract the data in a structed way so that I can get sales or commerce details. I'm struggling to construct the base and derived classes to store this API response because "AdditionalDetails" field data is not structured (Ideally the response should have sales and commerce property) but I don't have control over the external service.
In this example "AdditionalDetails" can contain either of records of Sales or Commerce but not both. The Sale and commerce has few fields in common like Id and channel and also has few field specific to sales and commerce. Can you suggest the better way to structure the class so that I can deserialize and get the sale and commerce ?. I can use the flat class to and have all the properties in additional details but would like to keep it clean.

[{
    "CustomerName": "Test",
    "CustomerId": "343434",
    "AdditionalDetails" : {
         "SalesId" : "43434",
         "SalesChannel": "Store",
         "SaleAmount": "34.53",
    }
        
},
{
    "CustomerName": "Test",
    "CustomerId": "343434",
    "AdditionalDetails" : {
         "CommerceId": "34343",
         "CommerceChannel": "channel1",
         "CommerceLicenseExpiration": "03/21/2021",
         "IsCommerceSalesEnabled": true
    }
        
}

]

CodePudding user response:

Maybe you could leverage the MissingMemberHandling setting from Json.Net

The idea is that you try to deserialize a section using 'sale' additional data schema and if you get an exception try to deserialize with the 'commerce' additional data schema.

Another idea is to deserialize into an dynamic/Expando object in C#, then try to read the values your expect from each schema and if present, fill an object. This deserialize as expandoObject may help

CodePudding user response:

You can use either generics

public class Customer<T>
{
    public string CustomerName { get; set; }
    public string CustomerId { get; set; }
    public T AdditionalDetails { get; set; }
}

or JObject

public class Customer
{
    public string CustomerName { get; set; }
    public string CustomerId { get; set; }
    public JObject AdditionalDetails { get; set; }
}

With both solutions, you can check for a property's existence and decide to which type of object to deserialize to. You can check for the existence with one of the different methods using Newtonsoft.Json library.

CodePudding user response:

If your class is customer and have an interface to IAdditionalDetails, you can read the details:

dynamic x = Newtonsoft.Json.JsonConvert.DeserializeObject(Jsondata);

And check whether exists SalesId or CommerceId to create your derived IAdditionalDetails class.

Another approach maybe create a class with all properties implementing ICustomerSales and ICustomerCommerce. Deserialize and check SalesId. If has value create your CustomerSales. If not, create your CustomerCommerce. You can create a static method to copy the interface properties and use it to copy from your deserialized object to your final class.

I like separate API structure from my real structure because usually they are different. Here is an example of a possible implementation:

First, the interfaces:

public interface ICustomer
{
    string Name { get; set; }
    int Id { get; set; }
}

public interface IAdditionalDetails
{
    int Id { get; set; }
    string Channel { get; set; }
}

public interface ISalesDetails : IAdditionalDetails
{
    double Amount { get; set; }
}

public interface ICommerceDetails : IAdditionalDetails
{
    DateTime LicenseExpiration { get; set; }
    bool Enabled { get; set; }
}

As you can see, Customer has a property Id and Name, not CustomerId and CustomerName.

The implementation:

public class Customer : ICustomer
{
    public string Name { get; set; }
    
    public int Id { get; set; }
}

public class AdditionalDetails : IAdditionalDetails
{
    public int Id { get; set; }
    public string Channel { get; set; }
}

public class SalesDetails : AdditionalDetails, ISalesDetails
{
    public double Amount { get; set; }
}

public class CommerceDetails : AdditionalDetails, ICommerceDetails
{
    public DateTime LicenseExpiration { get; set; }
    public bool Enabled { get; set; }
}

public class SalesCustomer : Customer
{
    public SalesCustomer()
    {
        this.SalesDetails = new SalesDetails();
    }

    public ISalesDetails SalesDetails { get; set; }
}

public class CommerceCustomer : Customer
{
    public CommerceCustomer()
    {
        this.CommerceDetails = new CommerceDetails();
    }

    public ICommerceDetails CommerceDetails { get; set; }
}

Usually, the previous classes has more methods to make your business work. And the Json part, totally isolated from your domain. A Details class with all the properties:

public class JsonAdditionalDetails
{
    public int? SalesId { get; set; }
    public string SalesChannel { get; set; }
    public double SaleAmount { get; set; }
    public int? CommerceId { get; set; }
    public string CommerceChannel { get; set; }
    public DateTime CommerceLicenseExpiration { get; set; }
    public bool IsCommerceSalesEnabled { get; set; }
}

And the Customer class:

public class JsonCustomer
{
    public string CustomerName { get; set; }
    public int CustomerId { get; set; }
    public JsonAdditionalDetails AdditionalDetails { get; set; }

    public ICustomer CreateCustomer()
    {
        if (this.AdditionalDetails.SalesId != null)
        {
            return new SalesCustomer
            {
                Id = this.CustomerId,
                Name = this.CustomerName,
                SalesDetails = new SalesDetails
                {
                    Id = this.AdditionalDetails.SalesId.Value
                    // Other properties
                }
            };
        }

        if (this.AdditionalDetails.CommerceId != null)
        {
            return new CommerceCustomer
            {
                Id = this.CustomerId,
                Name = this.CustomerName,
                CommerceDetails = new CommerceDetails
                {
                    Id = this.AdditionalDetails.CommerceId.Value
                    // Other properties
                }
            };
        }

        return null;
    }
}

In the Customer class you have a mapper to create your business objects.

In this form you have isolated the Json part and a total control over your business objects names and structure.

  • Related