簡體   English   中英

Expression.Convert:'System.Int64'類型的對象無法轉換為'System.Int32'類型

[英]Expression.Convert: Object of type 'System.Int64' cannot be converted to type 'System.Int32'

我昨天在這里問了一個關於從匿名對象中讀取屬性並將它們寫入類的私有字段的問題。 問題解決了。 這是一個短篇小說:

我有一些json格式的數據。 我將它們反序列化為ExpandoObject ,並將它們作為IDictionary<string, object>傳遞給方法。 它工作正常,除了Int32屬性。 看來他們改成了Int64 ,在哪里? 我不知道。

這是方法:

    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();
    }

如果我們有這個

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

class,並使用此對象

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

要使用上面的方法初始化Person的實例,會發生以下錯誤:

“System.Int64”類型的對象無法轉換為“System.Int32”類型。

但是,如果我們將Age屬性更改為:

public long Age { get; set; }

每件事看起來都很好,方法也很完美。 我完全混淆了為什么會這樣。 你有什么主意嗎?

所以你的輸入Dictionary包含long (基於評論中的討論)。

最簡單的解決方法是在SetValue之前添加Convert.ChangeType
(傳入sourceValueConstant(map.Field.FieldType)
但是,這可能會導致允許string -> int轉換的意外后果。

另一種方法是添加您自己的ConvertType方法,您可以在其中決定如何轉換類型。

表達是正確的。 問題是Json.NET 它將所有數值(匿名轉換)轉換為Int64 所以,我只需要一個自定義轉換器:

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();
    }
}

這是一個具體的實現,絕對可以實現更加擴展和有用。 但它只是解決了我目前的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM