Expression.Convert: an object of type 'System.Int64' cannot be converted to type 'System.Int32'

Yesterday, I asked yesterday about how to read the properties of an anonymous object and write them to the private fields of the class. The problem is resolved. Here is a short story:

I have some data in json format. I deserialize them to ExpandoObject and pass them as IDictionary<string, object> to the method. It works great except for the Int32 properties. It seems they are changing to Int64 , where? I dont know.

Here is the method again:

  private Func<IDictionary<string, object>, dynamic> MakeCreator( Type type, Expression ctor, IEnumerable<PropertyToFieldMapper> maps) { var list = new List<Expression>(); var vList = new List<ParameterExpression>(); // creating new target var targetVariable = Expression.Variable(type, "targetVariable"); vList.Add(targetVariable); list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type))); // accessing source var sourceType = typeof(IDictionary<string, object>); var sourceParameter = Expression.Parameter(sourceType, "sourceParameter"); // calling source ContainsKey(string) method var containsKeyMethodInfo = sourceType.GetMethod("ContainsKey", new[] { typeof(string) }); var accessSourceIndexerProp = sourceType.GetProperty("Item"); var accessSourceIndexerInfo = accessSourceIndexerProp.GetGetMethod(); // itrate over writers and add their Call to block var containsKeyMethodArgument = Expression.Variable(typeof(string), "containsKeyMethodArgument"); vList.Add(containsKeyMethodArgument); foreach (var map in maps) { list.Add(Expression.Assign(containsKeyMethodArgument, Expression.Constant(map.Property.Name))); var containsKeyMethodCall = Expression.Call(sourceParameter, containsKeyMethodInfo, new Expression[] { containsKeyMethodArgument }); // creating writer var sourceValue = Expression.Call(sourceParameter, accessSourceIndexerInfo, new Expression[] { containsKeyMethodArgument }); var setterInfo = map.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) }); var setterCall = Expression.Call(Expression.Constant(map.Field), setterInfo, new Expression[] { Expression.Convert(targetVariable, typeof(object)), Expression.Convert(sourceValue, typeof(object)) }); Console.WriteLine(Expression.Lambda(setterCall)); list.Add(Expression.IfThen(containsKeyMethodCall, setterCall)); } list.Add(targetVariable); var block = Expression.Block(vList, list); var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>( block, new[] { sourceParameter } ); return lambda.Compile(); } 

If we have it

 public class Person { public int Age { get; set; } public string Name { get; set; } } 

and use this object

 var data = new { Name = "Amiry", Age = 20 }; 

to initialize an instance of Person using the above method, this error occurs:

An object of type 'System.Int64' cannot be converted to type 'System.Int32'.

But if we change the Age property to:

 public long Age { get; set; } 

every thing looks great and the method works great. I am completely confused why this is happening. Do you have any ideas?

+4
source share
2 answers

The expression is correct. Json.NET task. It converts all numeric values ​​(in anonymous conversions) to Int64 . So, I just need a custom converter:

 public class JsonIntegerConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(IDictionary<string, object>); } public override bool CanWrite { get { return false; } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var result = new Dictionary<string, object>(); reader.Read(); while (reader.TokenType == JsonToken.PropertyName) { var propertyName = (string)reader.Value; reader.Read(); object value; if (reader.TokenType == JsonToken.Integer) { var temp = Convert.ToInt64(reader.Value); if (temp <= Byte.MaxValue && temp >= Byte.MinValue) value = Convert.ToByte(reader.Value); else if (temp >= Int16.MinValue && temp <= Int16.MaxValue) value = Convert.ToInt16(reader.Value); else if (temp >= Int32.MinValue && temp <= Int32.MaxValue) value = Convert.ToInt32(reader.Value); else value = temp; } else value = serializer.Deserialize(reader); result.Add(propertyName, value); reader.Read(); } return result; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotSupportedException(); } } 

This is a specific implementation and can absolutely be implemented more advanced and useful. But that just solves my current problem.

+1
source

So, your Dictionary entry contains long (based on the discussion in the comments).

The easiest fix is ​​to add Convert.ChangeType to SetValue .
(go to sourceValue and Constant(map.Field.FieldType) )
However, this may have unintended consequences of allowing string -> int .

An alternative is to add your own ConvertType method, where you decide how the types are converted.

+3
source

Source: https://habr.com/ru/post/1482084/


All Articles