[英]Expression.Convert: Object of type 'System.Int64' cannot be converted to type 'System.Int32'
I asked a question yesterday here about reading properties from an anonymous object and writing them to private fields of a class. 我昨天在这里问了一个关于从匿名对象中读取属性并将它们写入类的私有字段的问题。 The problem solved. 问题解决了。 Here is the short story: 这是一个短篇小说:
I have some data in json format. 我有一些json格式的数据。 I deserialize them to ExpandoObject
, and pass them as IDictionary<string, object>
to method. 我将它们反序列化为ExpandoObject
,并将它们作为IDictionary<string, object>
传递给方法。 It works fine, except Int32
properties. 它工作正常,除了Int32
属性。 It seems they change to Int64
, where? 看来他们改成了Int64
,在哪里? I don't 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 this 如果我们有这个
public class Person {
public int Age { get; set; }
public string Name { get; set; }
}
class, and use this object class,并使用此对象
var data = new { Name = "Amiry", Age = 20 };
to initialize an instance of Person
using above method, this error occurs: 要使用上面的方法初始化Person
的实例,会发生以下错误:
Object of type 'System.Int64' cannot be converted to type 'System.Int32'. “System.Int64”类型的对象无法转换为“System.Int32”类型。
But if we change Age
property to: 但是,如果我们将Age
属性更改为:
public long Age { get; set; }
every thing looks fine and method works perfectly. 每件事看起来都很好,方法也很完美。 I completely confused about why this happens. 我完全混淆了为什么会这样。 Do you have any idea? 你有什么主意吗?
So your input Dictionary
contains long
(based on discussion in comments). 所以你的输入Dictionary
包含long
(基于评论中的讨论)。
The easiest fix is to add Convert.ChangeType
before SetValue
. 最简单的解决方法是在SetValue
之前添加Convert.ChangeType
。
(passing in sourceValue
and Constant(map.Field.FieldType)
) (传入sourceValue
和Constant(map.Field.FieldType)
)
However, this may have an unintended consequence of allowing string -> int
conversion. 但是,这可能会导致允许string -> int
转换的意外后果。
Alternative is to add your own ConvertType
method, where you decide how types are converted. 另一种方法是添加您自己的ConvertType
方法,您可以在其中决定如何转换类型。
The expression is correct. 表达是正确的。 The problem is Json.NET
. 问题是Json.NET
。 It converts all numeric values (in anonymous conversions) to Int64
. 它将所有数值(匿名转换)转换为Int64
。 So, I just need a custom convertor: 所以,我只需要一个自定义转换器:
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 concrete implementation, and absolutely can be implemented more extended and useful. 这是一个具体的实现,绝对可以实现更加扩展和有用。 But it just solve my current problem. 但它只是解决了我目前的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.