Json.Net can't deserialize from dynamic object to nullable<short> - c#

I have the following JSON being sent to an MVC controller:
{
"CId": 374,
"CultureId": 1,
"VGS": null,
"DisplayOrder": 1
}
I am using JSON.Net to convert this to a dynamic object and later assigning the properties into an entity:
public partial class FooEntity
{
public short DisplayOrder { get; set; }
public Nullable<short> VGS { get; set; }
public short CId { get; set; }
public short CultureId { get; set; }
}
Notice that the VGS property we are assigning into is a nullable short, however when trying to create a new instance of the entity and assign the values, I get an error when trying to assign the VGS:
dynamic data = JsonConvert.DeserializeObject(payload);
var foo = new FooEntity();
foo.CId = data.CId;
foo.CultureId = data.CultureId;
foo.VGS = data.VGS; // Errors here
foo.DisplayOrder = data.DisplayOrder;
An exception of type 'System.FormatException' occurred in mscorlib.dll but was not handled in user code
Additional information: Input string was not in a correct format.
As far as I can tell, the json is correct to deserialize into a null value, and since I am assigning into a nullable value, I'm not sure what is causing the error?

JSON.Net does not know what to parse VGS to, which will make it an object. Then you cannot assign an object to VGS. Below is a working example. It can be solved in several ways:
Solution 1: Use explicit cast.
var payload = "{\"CId\": 374, \"CultureId\": 1,\"VGS\": null,\"DisplayOrder\": 1}";
dynamic dyn = JsonConvert.DeserializeObject<FooEntity>(payload);
var foo2 = new FooEntity();
foo.CId = dyn.CId;
foo.CultureId = dyn.CultureId;
foo.VGS = (short?)dyn.VGS; // Note the explicit cast.
foo.DisplayOrder = dyn.DisplayOrder;
Solution 2: Specify type.
var payload = "{\"CId\": 374, \"CultureId\": 1,\"VGS\": null,\"DisplayOrder\": 1}";
dynamic data = JsonConvert.DeserializeObject<FooEntity>(payload); // Specify type.
var foo = new FooEntity();
foo.CId = data.CId;
foo.CultureId = data.CultureId;
foo.VGS = data.VGS;
foo.DisplayOrder = data.DisplayOrder;
But then there's really no reason to use dynamic at all. You can serialize it directly to the entity you want.
FooEntity entity = JsonConvert.DeserializeObject<FooEntity>(payload);

Related

Serialize a JSON object without property name having a list of custom object in C#

I have model as below:
public class CustomModel
{
public string Data1 { get; set; }
public string Data2 { get; set; }
}
public class root
{
public List<CustomModel> data { get; set; }
}
My payload is as below:
List<CustomModel> cms = new List<CustomModel>();
CustomModel cm1 = new CustomModel();
cm1.Data1 = "a";
cm1.Data2 = "b";
cms.Add(cm1);
CustomModel cm2 = new CustomModel();
cm2.Data1 = "D";
cm2.Data2 = "E";
cms.Add(cm2);
BaseClass baseClass = new BaseClass();
baseClass.data = cms;
My JSON is:
var json = new JavaScriptSerializer().Serialize(baseClass);
And Result is:
{"data":[{"data1":"a","data2":"b"},{"data1":"D","data2":"E"}]}
BUT I need: without the "data" property as below:
{[{"data1":"a","data2":"b"},{"data1":"D","data2":"E"}]}
I tried the below function:
public static IEnumerable<object> GetPropertyValues<T>(T input)
{
return input.GetType()
.GetProperties()
.Select(p => p.GetValue(input));
}
Like
var value_only = GetPropertyValues(baseClass);
var json = new JavaScriptSerializer().Serialize(value_only);
BUT it returns [[{"data1":"a","data2":"b"},{"data1":"D","data2":"E"}]]
Is there anywasy to do it other than manually adding? Please help.
Note that {[{"data1":"a","data2":"b"},{"data1":"D","data2":"E"}]} is not valid json. In Json objects, data/values are organized by (named) properties.
However, here you seem to want a (root) Json object containing a value being Json array, but that value is not associated with a Json object property, thus not being valid Json.
If you really want invalid Json, i suggest you serialize the List<CustomModel> instance instead of the root model instance, resulting in a Json array, and then manually adding the surrounding { and } to the serialized Json string, thus giving you your desired invalid Json result:
var json = new JavaScriptSerializer().Serialize(baseClass.data);
var desiredResult = "{" + json + "}";
you don't need root class (or base class you must to deside what is name. Just use
var json = new JavaScriptSerializer().Serialize(cms);
PS.
And it is time to select a modern serializer.

WCF answer processing C#

There is a WCF service and I need to use it`s method.
method takes 3 parameters - string, DateTime, DateTime.
So my code is like this:
ServiceReference.LogsServiceClient myclient;
myclient = new ServiceReference.LogsServiceClient();
var response = myclient.GetHotPeriodLogs("somestring", dtFrom, dtTo);
========
Returned data type of method is some array (ServiceReference.TechLog[])
It seems the answer is the array of json responses.
So, I have the exception "Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1"
I should like to ask, what is the method to work with the answer?
Further, I need to insert each string of data into MSSQL DB, so I need to get sets of data. So, what I have to do?
ok. at first let us assume this is your ServiceReference.TechLog object:
public class TechLog
{
public DateTime CreationDate { get; set; }
public string Email { get; set; }
public bool IsApproved { get; set; }
}
now we need a json deserilzer method like this:
public static T JsonDeserializer<T>(string jsonString)
{
var settings = new JsonSerializerSettings { DateFormatHandling=DateFormatHandling.MicrosoftDateFormat };
var instance = JsonConvert.DeserializeObject<T>(jsonString, settings);
return instance;
}
that's all we need. now we can deserilze any thing like this:
var x = JsonDeserializer<TechLog[]>(response);

Subclass Reflection type error

I'm currently having some issues with a method I made. I use reflection to run through my class and get all it's properties. I use this to cast my models to DTO and vice-versa.
The problem I am encountering is that, whenever my class has another class as an attribute, I get an error.
Object of type 'UserTypeProxy' cannot be converted to type 'MyNamespace.DTO.UserTypeDto'.
This is my code:
public static T Cast<T>(object myobj)
{
Type _objectType = myobj.GetType();
Type target = typeof(T);
var x = Activator.CreateInstance(target, false);
var z = from source in _objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
var propy = myobj.GetType().GetProperty(memberInfo.Name);
value = propy.GetValue(myobj, null);
propertyInfo.SetValue(x, value, null); //<-- this is the line that gives the error
}
return (T)x;
}
As a previous commenter states, this is not the kind of code you should be writing/maintaining yourself. Frameworks like AutoMapper were built specifically to solve the problem you are attacking - converting model objects to DTOs. The right long-term choice would be to start leveraging such a framework instead of reinventing the wheel.
In the meanwhile the following code is a short-term solution for your issue. Keep in mind that while this may solve the specific case you mention in your question, object mapping has many corner cases and eventually you will run into another. I would recommend only using this as a temporary fix until you migrate to using AutoMapper or a similar framework.
Based on your description and your code, here is an example which models your failure:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This fails saying that UserModel cannot be converted to UserDto
var userDto = Cast<UserDto>(user);
}
class UserModel
{
public String Name { get; set; }
public int Age { get; set; }
public UserModel Buddy { get; set; }
}
class UserDto
{
public String Name { get; set; }
public int Age { get; set; }
public UserDto Buddy { get; set; }
}
The problem is that the Buddy property, unlike all the others, has a different type in the model and DTO classes. A UserModel is simply not assignable to a UserDto. The only exception to this is if the value is null.
For properties which are class types, instead of setting the target equal to the source you need to map the source type to the target type: UserModel -> UserDto. This can be done with a recursive call.
Before I show you the code which solves this issue, let's talk about naming for a minute. Calling your function Cast() is very misleading. The operation we are really doing here is taking some source object and mapping its property values onto some target object of a specific type (with possible recursive mappings for properties which are class types).
Given this terminology, here is some updated code which solves this specific issue:
public static T MapProperties<T>(object source)
{
return (T)MapProperties(source, typeof(T));
}
public static object MapProperties(object source, Type targetType)
{
object target = Activator.CreateInstance(targetType, nonPublic: false);
Type sourceType = source.GetType();
var sourcePropertyLookup = sourceType.GetProperties().ToDictionary(p => p.Name);
var targetPropertyLookup = targetType.GetProperties().ToDictionary(p => p.Name);
var commonProperties = targetPropertyLookup.Keys.Intersect(sourcePropertyLookup.Keys);
foreach (var commonProp in commonProperties)
{
PropertyInfo sourceProp = sourcePropertyLookup[commonProp];
PropertyInfo targetProp = targetPropertyLookup[commonProp];
object sourcePropValue = sourceProp.GetValue(source);
if(sourcePropValue == null || targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
targetProp.SetValue(target, sourceProp.GetValue(source));
}
else
{
object mappedValue = MapProperties(sourceProp.GetValue(source), targetProp.PropertyType);
targetProp.SetValue(target, mappedValue);
}
}
return target;
}
You can use this in the same way you've used your previous code:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This works!
var userDto = MapProperties<UserDto>(user);
}
Aside from some optimizations the key differences from your code is in the if-else block. There we check if we can assign the source value to the target directly, in which case we do what your code was doing so far. Otherwise it assumes we need to recursively map the value over. This new section is what solves the issue of converting a source property of a model class type to a target property of a DTO class type.

Error when casting variable

I get an error when I try to cast a query. This is the code of the query:
var query = (from article in db.V_CLIENT_PRIX
where article.CLIENT == Current_Client_Id
select new
{
ID = article.ID,
ARTICLE = article.Article,
REFERENCE = article.Reference,
REMISE = article.Remise,
PRIXVHT = article.PrixVHT,
CLIENT = article.CLIENT,
}
);
And I cast it like this:
ConventionList articlelistconvention = new ConventionList();
articlelistconvention = (ConventionList)query;
This is the code of my model:ConventionList
public class Commandelist
{
public string ARTICLE { get; set; }
public string CIN { get; set; }
public decimal STOCK { get; set; }
public string REFERENCE { get; set; }
public decimal PRIXVHT { get; set; }
public string IMAGE { get; set; }
public double QUANTITE { get; set; }
}
Can someone help me fix it?
You might be coming from a language with duck typing, like Javascript; however, in C# this is not possible. You can typically only cast objects if the interfaces and/or inheritance allows you to do so. The dynamic object you create in your Linq query will not share ancestry with the object you're trying to cast to.
In your specific code example though there is a quick fix:
var query = (
from article in db.V_CLIENT_PRIX
where article.CLIENT == Current_Client_Id
select new ConventionList // < --- change here!!
{
ID = article.ID,
ARTICLE = article.Article,
REFERENCE = article.Reference,
REMISE = article.Remise,
PRIXVHT = article.PrixVHT,
CLIENT = article.CLIENT,
});
However, getting this to work exactly for your scenario might require some tweaking, as your question is ambiguous about the difference / overlap between the dynamic object, the ConventionList class, and the CommandeList class.
You need to specify your type in the SELECT.
You can't cast anonymous types to declared types
You can't cast unrelated types to each other (command / convention) unless you just slopely pasted the wrong code for us to look at / figure out)
You can't cast List<T> of one type to another type, IEnemurable could work if the generics were related because of covariance.
Updated code with an explicit type in the constructor instead of an anonymous object, again I think you meant Convention but if not change it to the type you need.
var query = (
from article in db.V_CLIENT_PRIX
where article.CLIENT == Current_Client_Id
select new Convention()
{
ID = article.ID,
ARTICLE = article.Article,
REFERENCE = article.Reference,
REMISE = article.Remise,
PRIXVHT = article.PrixVHT,
CLIENT = article.CLIENT,
});

cannot cast expression of type 'System.Xml.Linq.XElement to type XXX

I am getting compiler error: cannot cast expression of type 'System.Xml.Linq.XElement to type AutomationStatusType
What am i doing wrong?
xml:
<Status>
<Version>33</Version>
<Status>Running</Status>
</Status>
query:
var query = (from status in doc.Descendants("Status")
select new AutomationStatus
{
Version = (string)status.Element("Version"),
Status = (AutomationStatusType)status.Element("Status"),
});
classes:
public class AutomationStatus
{
[XmlAttribute]
public string Version { get; set; }
[XmlElement]
public AutomationStatusType Status { get; set; }
}
[DataContract]
public enum AutomationStatusType
{
[EnumMember]
Idle,
[EnumMember]
Running
}
Edit:
after reading your comments, i indeed added the following casing:
Status = Enum.Parse(typeof(AutomationStatusType), (string)status.Element("Status")),
Now i am getting a compilation error:
Cannot convert type 'System.Xml.Linq.XElement' to 'Verint.AP2.Manager.AutomationStatusType'
However, if i create an anonymous class i am being able to get rid of the errors:
var query = (from status in doc.Descendants("AutomationStatus")
select new /*AutomationStatus*/
{
Version = (string)status.Element("Version"),
Status = Enum.Parse(typeof(AutomationStatusType), (string)status.Element("Status")),
TimeStamp = (DateTime) status.Element("TimeStamp")
});
What can be the issue, how can i create the class (non anonymous?)
Thanks!
Use the following snippet when you parse the status instead of the Row you have now.
Status = Enum.Parse(typeof(AutomationStatusType), status.Element("Status")
There is no type conversion operator between XElement and your enum. You need to convert the XElement to a string and parse the string to the enum:
{
Version = (string)status.Element("Version"),
Status = (AutomationStatusType)Enum.Parse(
typeof(AutomationStatusType), (string)status.Element("Status")),
}
though your code is not showing it im guessing that you are loading an XmlDoc or something.
use XmlSerializer instead.
something along the lines of:
XmlSerializer serializer = new XmlSerializer(typeof(AutomationStatus));
FileStream fs = new FileStream(filename, FileMode.Open);
AutomationStatus x;
x = (AutomationStatus) serializer.Deserialize(fs);

Categories