2

Closed

Unable to cast object of type 'System.Object' to type 'System.Collections.IList'.

description

Hi I'm using fastJSON to serialize and deseralize streams of, among other, kinect skeletons. Since it's message based I've used a Dictionary<string,object> to serialize the data - everthing works just like a charm except for deseralizing any message containing skeletons. It will then throw cast exception.

The class I use as container to serialize looks like this.

public sealed class MessageRecordProducer
{
// All except byte[] data goes here
public Dictionary<string,object> Items { get; set; }
// Used for byte[] arrays manually converting the strings using base64 decoder.
public Dictionary<string, object> Data { get; set; }  
}

the exception is thrown when:
var rec = JSON.Instance.ToObject<MessageRecordProducer>(bodystring);

and the bodystring in this case is attached as test.json

Cheers,
Mario

file attachments

Closed Jul 15, 2016 at 1:51 PM by MGholam

comments

MGholam wrote Feb 6, 2014 at 5:26 PM

Unfortunately I cannot check the json since I don't have the assemblies.

The error seems to because probably the Skeleton type has IList as a property and fastJSON doesn't work with interfaces this way when deserializing.

solo95 wrote Feb 6, 2014 at 9:51 PM

Hi and thanks for a prompt answer.

I've attached the kmv.filespersistence dll and the kinect.dll if you want to try :)

Do fastjson support serializing collection implementing IEnumerable? since all skeleton collections are implementing IEnumerable<CollectionType>, IEnumerable.

However they do expose a indexed property, can this be a problem, is there a workaround, can one intercept the serialization or do I need to do Parse and use those KV pairs to reconstruct the serialized object?

an example of indexer in a IEnumerable<T>,IEnumerable implementing ms kinect class:
public Joint this[JointType jointType] { get; set; }

Cheers,
Mario

MGholam wrote Feb 7, 2014 at 5:38 AM

I will try and take a look, in the meantime if you are on .net 4 try ToDynamic() and see if that helps.

solo95 wrote Feb 7, 2014 at 6:09 AM

Hi again,

I've replaces the current with the following (see below). However it seems like the type (in my case 2) is not introduced in the returned IList<Type> instead a IList<Object> (wanted IList<Skeleton>) - I'll do this search manually but it seems like a bug (or I'm using this completely insane way :) ?

Cheers,
Mario

( "Microsoft.Kinect.Skeleton, Microsoft.Kinect, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35": "2",)


var parse = JSON.Instance.Parse(bodystring);
  var dict = (IDictionary<string, object>)parse;
  var items = (Dictionary<string,object>)dict["Items"];

  object data;
  dict.TryGetValue("Data", out data);

  return new MessageRecordProducer { Data = data as Dictionary<string,object>, Items = items };

solo95 wrote Feb 7, 2014 at 6:31 AM

Hmm... after a bit more debugging i see its not an instance of Skeleton at all.

I'll try to look into this later this weekend and see, but I really apprechiate any help :)

Tanks again,
Mario

solo95 wrote Feb 7, 2014 at 9:00 AM

It seems that in CreateStringKeyDictionary: when it CreateGenericList it will use the parameters t1,t2 of which is the Dictionary<t1,t2> type - it looses the type information (in this case 2) - a fix would be check the type of the IList and use that type as the parameter?

Cheers,
Mario

solo95 wrote Feb 7, 2014 at 9:40 AM

I've managed to do a quick hack to create a list with type expected by adding:

val = CreateGenericList((List<object>)values.Value, GetTypeFromFirstClassInfo((List<object>)values.Value, globalTypes, t2), t1, globalTypes);
    private Type GetTypeFromFirstClassInfo(List<object> classInfos, Dictionary<string, object> globalTypes, Type defaultValue)
  {
    if (null == classInfos || 0 == classInfos.Count)
    {
      return defaultValue;
    }

    return GetTypeFromClassInfo((Dictionary<string, object>)classInfos[0],globalTypes, defaultValue);
  }

    private static Type GetTypeFromClassInfo(Dictionary<string, object> clz, Dictionary<string, object> globalTypes, Type defaulType)
  {
    object typeInfo;
    if (!clz.TryGetValue("$type", out typeInfo))
    {
      return defaulType;
    }

    return Reflection.Instance.GetTypeFromCache((string)globalTypes[(string)typeInfo]);
  }
And in CreateGenericList:
changed to IList col = (IList)Reflection.Instance.CreateGenericListInstance(pt);
  internal object CreateGenericListInstance(Type objType)
  {
    var listType = typeof(List<>);
    var constructedListType = listType.MakeGenericType(objType);

    return Activator.CreateInstance(constructedListType);
  }
That seems to work ok, however I stuck into a problem that I thought this deserializer could handle. The SkeletonPoint do not implement IConvertible and thus exception. However it's plain class (?) and the properties should be deserialized accordingly?

the json:
...
"Position": {
                "$type": "3",
                "X": 0.03670933,
                "Y": 0.4236084,
                "Z": 3.015385
            },
...

Whereas the type 3 = Microsoft.Kinect.SkeletonPoint, Microsoft.Kinect

It seems to me extra ordinary to add custom serializer and deserializer for this type or again - am I doing something wrong? :)

Cheers,
Mario

solo95 wrote Feb 7, 2014 at 11:48 AM

Hi, I've just checked the code again and found:
Fixed that using a overload on ChangeType since a check is already done if it's a dict<str,obj> - I just replaced the ParseDictionary method (there are 11 references in the code to ChangeType. Is this correct assumption.

Stumbeled into a new problem - the setter is null when a property is internal set - is it feasable to use reflection is such case? (of course a slowdown)...
  private object ChangeType(object value, Type conversionType, Dictionary<string,object> globaltypes)
  {
    var dict = value as Dictionary<string, object>;
    if (null == dict)
    {
      return ChangeType(value, conversionType);
    }


    return this.ParseDictionary(dict, globaltypes, conversionType, null /*createNew*/);
  }

solo95 wrote Feb 7, 2014 at 12:03 PM

I've "fixed" that to allow on public properties to have internal setters - however it does not deserialize the e.g. jointscollection properly - i give up! :)

Thanks for the supprt!

Cheers,
Mario