facebook-csharp-sdk / simple-json

JSON library for .NET 2.0+/SL4+/WP7/WindowsStore with optional support for dynamic and DataContract
MIT License
380 stars 143 forks source link

TargetParameterCountException deserializing Generic List (.NET 2.0) #37

Open hackedd opened 10 years ago

hackedd commented 10 years ago

When deserializing a Generic List (as in the example below), SimpleJson throws TargetParameterCountException when the Target framework for the Project is set to .NET 2.0.

using SimpleJson;

class Program
{
    public class Container
    {
        public List<int> items;
    }

    static void Main(string[] args)
    {
        string json = "{ \"items\": [ 1, 2, 3 ] }";
        Container obj;

        obj = SimpleJson.SimpleJson.DeserializeObject<Container>(json);
        Console.WriteLine(obj.items.Count);
    }
}

I think the problem is caused by ContructorDelegateFactory in SimpleJson.PocoJsonSerializerStrategy which calls ReflectionUtils.GetContructor to get a constructor for the generic List with no arguments. The constructor is later invoked (by DeserializeObject) with the number of items in the list as first and only argument.

If I change the code in ContructorDelegateFactory to account for Lists, it seems to work:

bool isArrayOrList = key.IsArray || ReflectionUtils.IsTypeGenericeCollectionInterface(key) || ReflectionUtils.IsAssignableFrom(typeof(IList), key);
return ReflectionUtils.GetContructor(key, isArrayOrList ? ArrayConstructorParameterTypes : EmptyTypes);
prabirshrestha commented 10 years ago

This bug seems to affects .net 4.0 too.

Seems like we have tests for int[] and List<CustomObject> but not for List<int> when T is of primitive types. https://github.com/facebook-csharp-sdk/simple-json/blob/master/src/SimpleJson.Tests/PocoDeserializerTests/ArrayTests.cs https://github.com/facebook-csharp-sdk/simple-json/blob/master/src/SimpleJson.Tests/PocoDeserializerTests/ListOfPocoDeserializeTests.cs

Could you create a unit test and send a PR.

Thanks.

Terricide commented 10 years ago

I fixed the issue for my project by modifying the code below. Adding the following to the GetConstructorInfo

            if (typeof(IList).IsAssignableFrom(type) && argsType.Length == 0)
            {
                argsType = new Type[] { typeof(int) };
            }

        public static ConstructorInfo GetConstructorInfo(Type type, params Type[] argsType)
        {
            IEnumerable<ConstructorInfo> constructorInfos = GetConstructors(type);
            int i;
            bool matches;

            if (typeof(IList).IsAssignableFrom(type) && argsType.Length == 0)
            {
                argsType = new Type[] { typeof(int) };
            }
            foreach (ConstructorInfo constructorInfo in constructorInfos)
            {
                ParameterInfo[] parameters = constructorInfo.GetParameters();
                if (argsType.Length != parameters.Length)
                    continue;

                i = 0;
                matches = true;
                foreach (ParameterInfo parameterInfo in constructorInfo.GetParameters())
                {
                    if (parameterInfo.ParameterType != argsType[i])
                    {
                        matches = false;
                        break;
                    }
                }

                if (matches)
                    return constructorInfo;
            }

            return null;
        }
prabirshrestha commented 10 years ago

@Terricide can u send a PR. You will first need to send a PR to the reflection-utils project. https://github.com/facebook-csharp-sdk/reflection-utils