JsonConvert.DeserializeObject defaults to first Enum item - c#

I have the following data object.
public class Response<T> where T : MyBaseDTO
{
public bool result
{
get; set;
}
public List<Message> messages
{
get; set;
}
public List<T> data
{
get; set;
}
}
public class Message
{
public Message(MessageTypeEnum type)
{
this.typeEnum = type;
}
public string type
{
get
{
return typeEnum.ToString();
}
}
public MessageTypeEnum typeEnum
{
get; set;
}
public int index
{
get; set;
}
public string field
{
get; set;
}
public string code
{
get; set;
}
public string message
{
get; set;
}
public string messageValue
{
get; set;
}
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
When I use RestSharp to call the API, the deserialized Data is null with following error(Content contains correct response in string):
" ErrorMessage "Each parameter in the deserialization constructor on type 'Response.Message' must bind to an object property or field on deserialization."
I found the following post and added empty constructor to my object.
Error: Each parameter in constructor must bind to an object property or field on deserialization
public class Message
{
public Message()
{
}
public Message(MessageTypeEnum type)
{
this.typeEnum = type;
}
public string type
{
get
{
return typeEnum.ToString();
}
}
public MessageTypeEnum typeEnum
{
get; set;
}
public int index
{
get; set;
}
public string field
{
get; set;
}
public string code
{
get; set;
}
public string message
{
get; set;
}
public string messageValue
{
get; set;
}
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
Added empty constructor resolved my issue with RestSharp. However I just noticed it failed some unit tests in my other project that uses Message Object.
Here is the code
using var reader = new StreamReader(result.ResponseMessage.Content.ReadAsStream());
body = reader.ReadToEnd();
Response<Test> responseObject = JsonConvert.DeserializeObject<Response<Test>>(body);
"body" looks like something below which is expected.
body "{\"result\":true,\"messages\":[{\"type\":\"ERROR\",\"index\":0,\"field\":\"ID\",\"code\":\"MISSING-ID\",\"message\":\"The entry is incomplete.",\"messageValue\":null}],\"data\":[{\"....
However, after the deserialization, "Type" became WARNING instead ERROR and hence it failed the unit test.
Looks like it defaults to first one of the Enum.
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
Does anyone knows what's going on here and how to fix the issue?

since you have type as string too, you have to fix your Message costructor by adding attribute
[JsonConstructor]
public Message(MessageTypeEnum type)
{
this.typeEnum = type;
}
after this everything is working properly

Your code is not complete and your JSON example is malformed, so I am unable to provide an example with your exact code. That said, you should be able to suss out the details you need to fix your particular issue.
First, when you are deserializing to a class you must have an empty constructor as required by the deserializer. The deserializer will not be passing any parameters to your constructor, so you can't depend on them.
Second, to read/write a class property it must have both a get and set. The deserializer will not be able to fully work with properties that do not have both get and set.
One possible solution, is to create a string property that contains both get and set, and then create an additional property with only the get that is typed to the Enum.
The root object is basically your Response object, but as you didn't provide that code I made the RootObject.
public class RootObject
{
public bool result { get; set; }
public Message[] messages { get; set; }
}
public class Message
{
// string property to allow deserialization
public string type
{
get; set;
}
// read only property typed to the enum that will
// refer to the 'type' property for conversion to the enum
public MessageTypeEnum typeEnum
{
get
{
switch (type.ToUpper())
{
case nameof(MessageTypeEnum.WARNING):
return MessageTypeEnum.WARNING;
case nameof(MessageTypeEnum.INFO):
return MessageTypeEnum.INFO;
case nameof(MessageTypeEnum.ERROR):
return MessageTypeEnum.ERROR;
default:
return MessageTypeEnum.UNKNOWN;
// return UNKNOWN or throw an out of range exception?
}
}
}
public int index
{
get; set;
}
public string field
{
get; set;
}
public string code
{
get; set;
}
public enum MessageTypeEnum
{
WARNING,
INFO,
ERROR
}
}

Related

Send a limited/reduced class to frontend

What I want
I want to send a limited/reduced class/object to frontend (as JSON). I use .NET Core 5.
What I have
I have a model class like this:
namespace Tasks.Models
{
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
+++ many more properties
}
}
Now depending on the which controller that calls this model I want to have different "kind" of models. So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
I tried setting up a method that "initialized the fields I wanted, but that also returned all properties
public static Responses FileResponse()
{
var response = new Responses()
{
Id = new Guid(),
Name = "",
Type = "File",
};
return response;
}
Now, when I call the Resources class or this method I get all properties, and returning it to the view presents all properties, but mostly as null, because I only set the three fields in the method.
What is the recommended way of solving this?
If you want to remove the field if it's null instead of showing in json with null value.
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Url { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
}
PS: Fiddle https://dotnetfiddle.net/hiMAci
It is just limiting the Resource class I am not able to do
Yep, side effect of C# being strongly typed, with object X definitely having properties Y and Z. You need differently shaped objects - either full on classes or records - that name the reduced set of properties because the serializer is going to look a tthe object and ser every property it can find.
You could make a new class for every variation - quick and easy with records, and easy to pass around inside your C#:
public record FileThing(string Id, string Type, string Name);
//make a new one and return it
new FileThing(someResources.Id, someResources.Type, someResources.Name);
Or can consider using an anonymous type if you're literally looking to put a few properties into some json, down a socket to a consuming front end (I can't quite decide what you mean by "view" - it doesn't seem to be an MVC View) that only cares about a few props out of many
So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
public ActionResult SomeControllerMethod(){
if(isFile)
return Ok(new { someResources.Id, someResources.Type, someResources.Name });
else if(isUrl)
return Ok(new { someResources.Id, someResources.Url, someResources.Name });
}
Anonymous types are a bit harder to work with because the compiler writes the class for you, so it's tricky to do things like declare return types from methods if the method is returning an AT.. But if you're using it as some fill-in all within one method, such as a "make this and serialize it", they work well..
I think your approach is not the right one here. I tend to follow more general OO guidelines in this situation (note, some consider these a bit dated, and other solutions exist. But they are still commonly used)
You write against an interface. So let's see what you want... A guid, type and name. All other deatils aren't important.
public interface IResourceDetails
{
public Guid Id { get; }
public string Name { get; }
public string Type { get; }
}
And you can have multiple of these interfaces.
You could then implement the interfaces per type. But I would probably combine them in a base class
public abstract class ResourceBase : IResourceDetails
{
public Guid Id { get; } = new ();
public string Name { get; init; }
public string Type { get; }
public ResourceBase(string type)
{
Type = type;
}
}
Each resource type would have it's own implementation
public class FileResource : ResourceBase
{
public FileResource() : base("File") { }
// File-specific properties.
public string Description { get; init; }
public DateTime? Createdon { get; init; }
}
The response method then could be made generic and look like this
public static IActionResult Response(IResourceDetails resource)
{
return Ok(new
{
resource.Id,
resource.Name,
resource.Type,
});
}

c# Generic property

I am trying to make a class in C# that can be used to return data of any types.
public class ResponseObject
{
public <T> data { get;set }
public Boolean Success { get; set; }
public string Message { get; set; }
}
The Object will be a wrapper for the response object when my application sends a request to the API.
i have tried researching this but cannot find any tutorials which are relevant to what i am trying to do.
Is this possible in C#? the Response Object will be converted to a JSON string and then sent as a response.
I will not be doing any processing of this object as that will already by done. I just want to place the data inside the ResponseObject and send it
I want to do something along the lines of:
var customers = repository.GetCustomers();
var orders = repository.GetOrders();
if(customers)
{
success = true;
message = "";
}
else{
success = false;
message = "failed to get customers";
}
if(orders)
{
orderssuccess = true;
ordersmessage = "";
}
else{
orderssuccess = false;
ordersmessage = "failed to get orders";
}
ResponseObject customerResponse = new ResponseObject{
data = customers,
success = success,
message = message
};
ResponseObject orderResponse = new ResponseObject{
data = orders,
success = orderssuccess,
message = ordersmessage
};
You need to add <T> to the class and use T as the type of your property.
public class ResponseObject<T>
{
public T data { get; set; }
public Boolean Success { get; set; }
public string Message { get; set; }
}
You have almost done it already! Just change your <T> to T.
public class ResponseObject<T> where T : class
{
public T data { get; set; }
public Boolean Success { get; set; }
public string Message { get; set; }
}
Here where T : class ensure that the generic type parameter is a reference type. From your question it seems you are going to pass in an object there.
You have two options here:
Make the class generic, or
Use generic methods for accessing the property
Here are the examples of both approaches:
// Make the class generic
public class ResponseObject<T> {
public T Data { get; set }
public Boolean Success { get; set; }
public string Message { get; set; }
}
// Use generic methods to access the property
public class ResponseObject {
private object data;
public T GetData<T>() {
return (T)data;
}
public void SetData<T>(T newData) {
data = newData;
}
public Boolean Success { get; set; }
public string Message { get; set; }
}
Second approach is less robust than the first one - basically, it's a glorified unrestricted cast. First approach, however, does not let you build a container of ResponseObjects with different Ts. You can address this problem by adding an interface on top of ResponseObject:
interface IResponseObject {
object DataObj { get; set }
Type ObjType { get; }
bool Success { get; set; }
string Message { get; set; }
}
public class ResponseObject<T> {
public T Data { get; set }
public ObjType => typeof(T);
public DataObj {
get => Data;
set => Data = value; // C# 7 syntax
}
public Boolean Success { get; set; }
public string Message { get; set; }
}

newtonsoft json deserializer not working for property with no set

I'm facing a null exception when de-serializing the class below:
[Serializable]
public class myClass
{
public IList<string> ResidentialAddressToPrint
{
get
{
var list = new List<string>();
if (!string.IsNullOrWhiteSpace(ResidentialAddress.AddressLine1))
list.Add(ResidentialAddress.AddressLine1);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.AddressLine2))
list.Add(ResidentialAddress.AddressLine2);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.Suburb))
list.Add(ResidentialAddress.Suburb);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.State))
list.Add(ResidentialAddress.State);
if (!string.IsNullOrWhiteSpace(ResidentialAddress.Postcode))
list.Add(ResidentialAddress.Postcode);
return list;
}
}
[Serializable]
public class RegisterRebateAddressModel
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string Suburb { get; set; }
public string State { get; set; }
public string Postcode { get; set; }
}
*Newtonsoft.Json.JsonSerializationException: Error getting value from 'myClass'. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at myClass.get_ResidentialAddressToPrint()*
Can someone clarify below:
Does newtonsoft requires the mandatory Set property?
Also noticed it fails for private set; property. Is this feature by design.
What's the best way to solve this issue as we have many properties with no setter.

JSON Deserializer "loses" last property of JSON value

passing a Json value like this one(this will be the var jsonValue in code):
"{\"Something\":0,\"Something2\":10,\"Something3\":{\"Something4\":17,\"Something5\":38042,\"Something6\":38043,\"Id\":215},\"Something7\":215,\"SomethingId\":42,\"Something8\":\"AString, Gläser\",\"Something8\":\"44-55-18\",\"Status\":{\"Caption\":\"Fixed\",\"Value\":7},\"Type\":\"Article\",\"Id\":97,\"#Delete\":true,\"Something9\":\"8\"}"
to the following code:
var deserializer = new JsonSerializer();
const string regex = #"/Date\((.*?)\+(.*?)\)/";
var reader = new JsonTextReader(new StringReader(jsonValue));
returnValue = deserializer.Deserialize(reader, type);
type is the typeof https://dotnetfiddle.net/LMPEl0 (thank you Craig) (sorry for the weird names, can't disclose the actual ones...)
The jsonvalue is generated by input in an editable cell of a DataTable and apparently places previously null values in the end of the json string.
I get a null value in the "Something9" property in the returnValue, instead of 8(Something9 was null before and set to 8 through an editable Cell of a DataTable)
Is there some problem with the Json value that I can't see?
Or do I need some setting in the Deserializer?
Thanks
You don't show what your type is so I generated one using http://json2csharp.com.
public class Something3
{
public int Something4 { get; set; }
public int Something5 { get; set; }
public int Something6 { get; set; }
public int Id { get; set; }
}
public class Status
{
public string Caption { get; set; }
public int Value { get; set; }
}
public class RootObject
{
public int Something { get; set; }
public int Something2 { get; set; }
public Something3 Something3 { get; set; }
public int Something7 { get; set; }
public int SomethingId { get; set; }
public string Something8 { get; set; }
public Status Status { get; set; }
public string Type { get; set; }
public int Id { get; set; }
[JsonProperty("#Delete")]
public bool Delete { get; set; }
public string Something9 { get; set; }
}
Because one of your properties has a name that is not valid as a .NET property I added the [JsonProperty] attribute to that one. After that it worked perfectly. Perhaps the problem is with how you declared the #Delete JSON property in your .NET type. Given that Something9 comes after that property it would be my guess that that's part of the problem.
Here's the fiddle.
https://dotnetfiddle.net/McZF9Q
While Craig's answer helped a lot and finally led to a solution the exact answer to the problem was the following:
The Status object is an Enum and was not Deserialized correctly.
Due to that, anything that followed in the Json string was also not deserialized.
Implementing a custom Enum Deserializer was the solution. There are other Questions in stackoverflow that helped with this, particularly this one here:
How can I ignore unknown enum values during json deserialization?
Thank you everyone :)

Hide enum values in derived class

I have the following
public enum MessageType
{
Warning,
Info,
Error
}
public class CalculationMessage
{
public string Message { get; set; }
public MessageType Type { get; set; }
}
public class ValidationMessage
{
public string Message { get; set; }
public MessageType Type { get; set; }
public string ErrorValue { get; set; }
}
I am trying to create a base class from which both of these classes are derived, however I have a problem with the enum as a ValidationMessage can be Error / Warning / Info but a CalculationMessage can only be Warning or Info.
How is this best achieved?
Thanks in advance.
You could add a parameter validation in the setter:
set
{
if(value == MessageType.Warning || value == MessageType.Info)
{
this.messageType = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
This is, however, a violation of the Liskov Substitution Principle. Therefore be careful and think if there may be a way around (e.g. not making the setter public at all, but determing the MessageType internally).

Categories