Deserialization and serialization using newtonsoft c# - c#

My application is binding a REST API, that returns this to me:
{
key: "XXXX-XXXX",
fields: {
customfield_10913: {
value: "L2"
}
}
}
I'm using Newtonsoft JSON to serialize and deserialize and I've created these models to make it work:
public class Issue
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("fields")]
public Fields Fields { get; set; }
}
public class Fields
{
[JsonProperty("customfield_10913")]
public CustomField Level { get; set; }
}
public class CustomField
{
[JsonProperty("value")]
public string Value{ get; set; }
}
The application is deserializing everything ok, using this code:
T model = JsonConvert.DeserializeObject<T>(result);
After a lot of business logic, my WEB API should return a new JSON:
protected T Get()
{
return model;
}
And I've got everything like the JSON I've read from another API.
So, What I need?
I need to read the field CUSTOM_FIELDXXX, but I can't return it with this name in my WEB API. How could I read this field, but when I'm doing the serialization, it assume another one?

You may try below function
Issue model = deserializeObject<Issue>(result);
public T deserializeObject<T>(string result)
{
try
{
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
var items = (T)JsonConvert.DeserializeObject(result, typeof(T), settings);
return items;
}
catch (Exception ex)
{
throw ex;
}
finally
{
}
}

If you can have any property name for your CustomField property, you can serialize that as a Dictionary<string, CustomField>, like so:
public class Issue
{
public Issue()
{
this.Fields = new Dictionary<string, CustomField>();
}
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("fields")]
public Dictionary<string, CustomField> Fields { get; set; }
}
Then you can use any string-valued name for your custom field.
Working fiddle.

Related

How can I send JSON response to client from ASP.NET Core Web API?

I am new to .NET Core. I am creating an API which returns some data in response. I have created a utility function successResponse to return a generic success response with data to client.
Here is the model class of that successResponse
public class SuccessResponse
{
public object? payload { get; set; } = null;
public bool success { get; set; } = false;
public SuccessResponse(object data, bool isSuccess)
{
payload = data;
success = isSuccess;
}
}
I have also created a helper class that have a function to return successResponse like this
public static class ResponseHandler
{
public static SuccessResponse successResponse(object data)
{
return new SuccessResponse(data, true);
}
public static ErrorResponse errorResponse(string error)
{
return new ErrorResponse(error);
}
}
In my controller, I have code like this:
[HttpPost]
public async Task<IActionResult> GetIncomingFile(IFormFile file)
{
try
{
var options = new JsonSerializerOptions { IncludeFields = true };
List<ImportedFileData> importedExcelFileData = await ExcelMapperFileReader.getFileData(file);
BalanceSheet balanceSheetData = BalanceSheetReport.createBalanceSheet(importedExcelFileData);
return Ok(ResponseHandler.successResponse(JsonSerializer.Serialize(balanceSheetData, options)));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return BadRequest(ResponseHandler.errorResponse(ex.Message));
}
}
My model class BalanceSheet is like this
public class BalanceSheet
{
public BalanceSheetAssets assets = null!;
public BalanceSheetLiabilities liabilities = null!;
public BalanceSheetEquity equity = null!;
public BalanceSheet(BalanceSheetAssets incomingAssets, BalanceSheetLiabilities incomingLiabilities, BalanceSheetEquity incomingEquity)
{
assets = incomingAssets;
liabilities = incomingLiabilities;
equity = incomingEquity;
}
}
The problem is that I get this response on the client:
{
"payload": "{\"assets\":{\"currentAssets\":[{\"title\":\"Inventory\",\"amount\":85300,\"code\":\"CA\"},{\"title\":\"Accounts Receivable\",\"amount\":4700,\"code\":\"CA\"},{\"title\":\"Cash\",\"amount\":5000,\"code\":\"CA\"}],\"nonCurrentAssets\":[{\"title\":\"Furniture\",\"amount\":200000,\"code\":\"NCA\"}],\"totalAssets\":255000},\"laibilities\":{\"currentLiabilities\":[{\"title\":\"Inventory\",\"amount\":85300,\"code\":\"CA\"},{\"title\":\"Accounts Receivable\",\"amount\":4700,\"code\":\"CA\"},{\"title\":\"Cash\",\"amount\":5000,\"code\":\"CA\"}],\"nonCurrentLiabilities\":[{\"title\":\"Furniture\",\"amount\":200000,\"code\":\"NCA\"}],\"totalLiabilities\":45000},\"equity\":{\"equityList\":[{\"title\":\"Equity\",\"amount\":150000,\"code\":\"EQ\"},{\"title\":\"Retained Earnings\",\"amount\":60000,\"code\":\"EQ\"}],\"totalEquity\":210000}}",
"success": true
}
But I want to receive correctly formatted JSON response at the client.
How can I do that?
You are getting an empty object because System.Text.Json, by default, ignores fields as documented here.
The BalanceSheet class is designed to have public fields, so you get an empty object in the response.
You have a couple of options:
you can instruct System.Text.Json to include fields by adding the JsonIncludeAttribute to all the public fields of the BalanceSheet class. See here for more details.
you can chage the design of the BalanceSheet class and use properties instead of fields.
In options 1 you need to change the code this way:
public class BalanceSheet
{
[JsonInclude]
public BalanceSheetAssets assets = null!;
[JsonInclude]
public BalanceSheetLiabilities laibilities = null!;
[JsonInclude]
public BalanceSheetEquity equity = null!;
public BalanceSheet(
BalanceSheetAssets incomingAssets,
BalanceSheetLiabilities incomingLaibilities,
BalanceSheetEquity incomingEquity) {
assets = incomingAssets;
laibilities = incomingLaibilities;
equity = incomingEquity;
}
}
In options 2 you need to change the code this way:
public class BalanceSheet
{
public BalanceSheetAssets Assets { get; } = null!;
public BalanceSheetLiabilities Laibilities { get; } = null!;
public BalanceSheetEquity Equity { get; } = null!;
public BalanceSheet(
BalanceSheetAssets incomingAssets,
BalanceSheetLiabilities incomingLaibilities,
BalanceSheetEquity incomingEquity) {
Assets = incomingAssets;
Laibilities = incomingLaibilities;
Equity = incomingEquity;
}
}
In general using public fields is a bad design, so I would go with option 2. See here for more details.
Either way you need to let ASP.NET core to do the JSON serialization for you, so change the return statement in the action method this way:
return Ok(ResponseHandler.succesResponse(balanceSheetData));
In my comment I suggested you to manually serialize to JSON just as a debugging step, to help us understanding the nature of your issue. You should never manually serialize the response object to JSON: this is done automatically for you by the ASP.NET core framework, when the OkObjectResult is executed.

Convert class to dynamic and add properties

I have a class MyClass. I would like to convert this to a dynamic object so I can add a property.
This is what I had hoped for:
dynamic dto = Factory.Create(id);
dto.newProperty = "123";
I get the error:
WEB.Models.MyClass does not contain a definition for 'newProperty'
Is that not possible?
The following has worked for me in the past:
It allows you to convert any object to an Expando object.
public static dynamic ToDynamic<T>(this T obj)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var propertyInfo in typeof(T).GetProperties())
{
var currentValue = propertyInfo.GetValue(obj);
expando.Add(propertyInfo.Name, currentValue);
}
return expando as ExpandoObject;
}
Based on: http://geekswithblogs.net/Nettuce/archive/2012/06/02/convert-dynamic-to-type-and-convert-type-to-dynamic.aspx
As my object has JSON specific naming, I came up with this as an alternative:
public static dynamic ToDynamic(this object obj)
{
var json = JsonConvert.SerializeObject(obj);
return JsonConvert.DeserializeObject(json, typeof(ExpandoObject));
}
For me the results worked great:
Model:
public partial class Settings
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("runTime")]
public TimeSpan RunTime { get; set; }
[JsonProperty("retryInterval")]
public TimeSpan RetryInterval { get; set; }
[JsonProperty("retryCutoffTime")]
public TimeSpan RetryCutoffTime { get; set; }
[JsonProperty("cjisUrl")]
public string CjisUrl { get; set; }
[JsonProperty("cjisUserName")]
public string CjisUserName { get; set; }
[JsonIgnore]
public string CjisPassword { get; set; }
[JsonProperty("importDirectory")]
public string ImportDirectory { get; set; }
[JsonProperty("exportDirectory")]
public string ExportDirectory { get; set; }
[JsonProperty("exportFilename")]
public string ExportFilename { get; set; }
[JsonProperty("jMShareDirectory")]
public string JMShareDirectory { get; set; }
[JsonIgnore]
public string Database { get; set; }
}
I used it in this manner:
private static dynamic DynamicSettings(Settings settings)
{
var settingsDyn = settings.ToDynamic();
if (settingsDyn == null)
return settings;
settingsDyn.guid = Guid.NewGuid();
return settingsDyn;
}
And received this as a result:
{
"id": 1,
"runTime": "07:00:00",
"retryInterval": "00:05:00",
"retryCutoffTime": "09:00:00",
"cjisUrl": "xxxxxx",
"cjisUserName": "xxxxx",
"importDirectory": "import",
"exportDirectory": "output",
"exportFilename": "xxxx.xml",
"jMShareDirectory": "xxxxxxxx",
"guid": "210d936e-4b93-43dc-9866-4bbad4abd7e7"
}
I don't know about speed, I mean it is serializing and deserializing, but for my use it has been great. A lot of flexability like hiding properties with JsonIgnore.
Note: xxxxx above is redaction. :)
You cannot add members to class instances on the fly.
But you can use ExpandoObject. Use factory to create new one and initialize it with properties which you have in MyClass:
public static ExpandoObject Create(int id)
{
dynamic obj = new ExpandoObject();
obj.Id = id;
obj.CreatedAt = DateTime.Now;
// etc
return obj;
}
Then you can add new members:
dynamic dto = Factory.Create(id);
dto.newProperty = "123";
You can't add properties to types at runtime. However, there is an exception which is: ExpandoObject. So if you need to add properties at runtime you should use ExpandoObject, other types don't support this.
Just to add up my experience, if you are using JSON.NET, then below might be one of the solution:
var obj....//let obj any object
ExpandoObject expandoObject= JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj));
Not tested performances etc.. but works.

Serializing a custom object using SOLID principles

I want to serialize my model objects (from WPF MVVM) which contains pure data. This sounds easy but I don't want to use the Serialization attributes and stuff provided in .NET framework. I just want to serialize it using my own way.
So here's a simplified version of one of my classes.
public class EntryKeyValuePair
{
public EntryKeyValuePair(string key, string value, bool isMultiline = false, bool isMandatory = true, bool isProtected = false)
{
Key = key;
Value = value;
IsMultiline = isMultiline;
IsMandatory = isMandatory;
IsProtected = isProtected;
}
public string Key { get; set; }
public string Value { get; set; }
public bool IsMultiline { get; set; }
public bool IsMandatory { get; set; }
public bool IsProtected { get; set; }
public static EntryKeyValuePair FromXML(XElement element, ICipher cipher)
{
string key = cipher.Decrypt(element.Element(nameof(Key)).Value);
string value = cipher.Decrypt(element.Element(nameof(Value)).Value);
bool isMultiline = bool.Parse(element.Element(nameof(IsMultiline)).Value);
bool isMandatory = bool.Parse(element.Element(nameof(IsMandatory)).Value);
bool isProtected = bool.Parse(element.Element(nameof(IsProtected)).Value);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
public XElement ToXML(ICipher cipher)
{
return new XElement(nameof(EntryKeyValuePair),
new XElement(nameof(Key),cipher.Encrypt(Key)),
new XElement(nameof(Value), cipher.Encrypt(Value)),
new XElement(nameof(IsMultiline), IsMultiline), new XElement(nameof(IsMandatory), IsMandatory),
new XElement(nameof(IsProtected), IsProtected));
}
}
This works quite well. But this violates single responsibility principle and maybe other principles as well. This is also difficult to maintain and extend.
So I wanted to find another way. And here it is:
First I defined an IStringFormatter interface which can format the data to any string data formats like XML and JSON. (Not sure tho)
interface IStringFormatter
{
string Name { get; set; }
Dictionary<string, string> FieldDictionary { get; }
string Format();
}
Here's how the XMLStringFormatter looks like:
class XmlStringFormatter : IStringFormatter
{
public XmlStringFormatter()
{
FieldDictionary = new Dictionary<string, string>();
}
public string Name { get; set; }
public Dictionary<string, string> FieldDictionary { get; }
public string Format()
{
var xElement = new XElement(Name, FieldDictionary.Keys.Select(key => new XElement(key, FieldDictionary[key])));
return xElement.ToString();
}
}
Then I defined an ISerializer to serialize (or rather save) my data objects to the IStringFormatter.
interface ISerializer<T>
{
T DeSerialize(IStringFormatter stringFormatter);
void Serialize(T obj, IStringFormatter stringFormatter);
}
And here is how I "Serialize" EntryKeyValurPair by implementing this:
internal class EntryKeyValurPairSerializer : ISerializer<EntryKeyValuePair>
{
public EntryKeyValuePair DeSerialize(IStringFormatter stringFormatter)
{
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
try {
string key = fieldDictionary[nameof(EntryKeyValuePair.Key)];
string value = fieldDictionary[nameof(EntryKeyValuePair.Value)];
bool isMandatory = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMandatory)]);
bool isProtected = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsProtected)]);
bool isMultiline = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMultiline)]);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
catch (KeyNotFoundException ex) {
throw new SerializationException(ex);
}
catch (FormatException ex) {
throw new SerializationException(ex);
}
}
public void Serialize(EntryKeyValuePair obj, IStringFormatter stringFormatter)
{
stringFormatter.Name = nameof(EntryKeyValuePair);
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
fieldDictionary.Add(nameof(EntryKeyValuePair.Key), obj.Key);
fieldDictionary.Add(nameof(EntryKeyValuePair.Value), obj.Value);
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMandatory), obj.IsMandatory.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsProtected), obj.IsProtected.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMultiline), obj.IsMultiline.ToString());
}
}
Now this works fine. But the problem is when I have a complex type like List<Entry> (where Entry is another data class) in my data classes.
As the IStringFormatter contains a Dictionary<string, string>, I can't just convert a List<Entry> to a string because I don't know what kind of IStringFormatter it wants in the context of ISerializer. How can I fix this? I also want to know if my solution is adhering to SOLID principles. If you can suggest a better solution (NOT the typical .NET serialization), I would appreciate it.
Writing your own serializer might be an interesting task, but I doubt that this is a good idea.
As I understood you want to keep your models clean, without any serialization specific attributes. I guess by "typical .NET serialization" you mean methods included with .Net framework.
For simplicity we will use these simple classes as an example:
class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public List<Order> Orders { get; set; }
}
class Order
{
public int Id { get; set; }
public string Details { get; set; }
}
An easy option would be to use Json.NET:
var customer = new Customer
{
Name = "Darth Vader",
Age = 45,
Orders = new List<Order>
{
new Order { Id = 1, Details = "Order1" },
new Order { Id = 2, Details = "Order2" }
}
};
string json = JsonConvert.SerializeObject(customer);
So as you can see you don't need to add any custom attributes to Customer class. It will work until you want to serialize all public properties.
The resulting JSON will be:
{
"Name": "Darth Vader",
"Age": 45,
"Orders": [
{
"Id": 1,
"Details": "Order1"
},
{
"Id": 2,
"Details": "Order2"
}
]
}
After that you can always deserialize it:
var customer = JsonConvert.DeserializeObject<Customer>(json);
Lets say that you don't want Age property to be included. In this case I would suggest to create a different class that will be used for serialization only:
class CostomerSerializationContract
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
This main advantage if this approach is that you have separate class for serialization, and you can add any custom attributes there, if you choose to use some other serializer, without violating SOLID principle. The main disadvantage is that you need to keep both objects in sync manually.
You can use AutoMapper to reduce manual work when creating serialization contract from source class.

Set JsonProperty PropertyName dynamically

I' m developing some WebAPI and have to return HttpResponseMessage with 2 standart service fields and one field which contains an array. Something like this:
{
field1:"test",
field2:123,
array:[{},{}]
}
But actually i need several responses which should differ only in name of array property:
{
field1:"test",
field2:123,
someArray:[{},{}]
}
and
{
field1:"test",
field2:123,
someOtherArray:[{},{}]
}
Type of elemnts in array is the same in each response. So I want to create one class for such type of responses and just change its PropertyName. Like this one:
public class Response
{
public int field2 { get; set; }
public string field1 { get; set; }
[JsonProperty(PropertyName = ???)]
public IEnumerable<SomeType> Array { get; set; }
}
How can I implement it?
You could also consider using a dictionary.
[HttpGet, Route("api/yourroute")]
public IHttpActionResult GetSomeData()
{
try
{
var data = new Dictionary<string, dynamic>();
data.Add("field1", "test");
data.Add("field2", 123);
var fieldName = someCondition == true? "someArray" : "someOtherArray";
data.Add(fieldName, yourArray);
return Ok(data);
}
catch
{
return InternalServerError();
}
}

Deserializing just a single node of a JSON response

I have a json response like
{
"appStatus":{
"status":true
},
"lastSyncDate":"06-07-2013 13.54.27",
"configResponse":{
"status":{
"status":true
},
"configs":{
"APPLOGENABLED":{
"configCode":"APPLOGENABLED",
"configDesc":"enable or disable logging from front end",
"configType":"TAB",
"configValue":"Y"
},
"COMMENTSTIMER":{
"configCode":"COMMENTSTIMER",
"configDesc":"timer for comments in seconds",
"configType":"TAB",
"configValue":"60"
},
"SUMMARYTIMER":{
"configCode":"SUMMARYTIMER",
"configDesc":"timer for summary in seconds",
"configType":"TAB",
"configValue":"30"
},
"ALERTSTIMER":{
"configCode":"ALERTSTIMER",
"configDesc":"timer for alerts in seconds",
"configType":"TAB",
"configValue":"60"
}
},
"lastSyncDate":"06/07/2013 13.48.13"
}
}
Using json.NET, I want to extract configResponse into dictionary. I know i can directly convert a Dictionary object like this
JsonConvert.DeserializeObject<Dictionary<string,string>>()....
but since configResponse is a sub element, I am not able to parse it as required.
I want to deserialize the above json response as
public class ConfigResponse
{
public Status status { get; set; }
public Dictionary<string, ConfigurationItem> configs { get; set; }
public string lastSyncDate { get; set; }
}
public class ConfigurationItem
{
public String configCode { get; set; }
public String configDesc { get; set; }
public String configType { get; set; }
public String configValue { get; set; }
}
You can parse JSON string into JObject, get sub-object "configResponse", then deserialize it into ConfigResponse. It's one line of code:
JObject.Parse("{...}")["configResponse"].ToObject<ConfigResponse>()
If you need a custom serializer to set deserialization options, you can pass it to ToObject<T>() method.
i have only use this method and it worked.
await JsonConvert.DeserializeObjectAsync<ConfigResponse>(jsontoobject, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });

Categories