mgholam / fastJSON

Smallest, fastest polymorphic JSON serializer
https://www.codeproject.com/Articles/159450/fastJSON-Smallest-Fastest-Polymorphic-JSON-Seriali
MIT License
478 stars 148 forks source link

ArgumentNullException: Value cannot be null. Parameter name: con #112

Open frankinstien opened 4 years ago

frankinstien commented 4 years ago

I tried to deserialize using this code:

fastJSON.JSON.ToObject<T>(jSon);

The Json is below: {"$types":{"Model.RootDescriptor, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"1","Models.MicroDescriptor, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"2","Models.ProtoAttribute, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"3","Models.EnumElement, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"4","Models.EnumClass, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"5","AIDataModels.Boundary.MulitDimensionalBoundary, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"6","AIDataModels.Miscelaneous_Models.MaxMidMin, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"7","AIDataModels.Summaries.ClassSummary, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"8","AIDataModels.Summaries.GeneralizeTypeSummary, AIDataModels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"9"},"$type":"1","WordKey":"Agony","WordReference":"tLFAwvRiz0qp1dyoYLLEWA==","GrammerType":"adjective","Descriptors":[{"$type":"2","Attributes":[{"$type":"3","AttributeType":"value","Description":"Attribute Type","Data":"vector","Parent":"NFCspbOyfkaCyfWrYAxbYA==","BaseID":"uUxS+vsNakm9R82J9MYasg==","ID":"bG0JWCWwrkCCJyrZCn+gTQ==","IndexOcurrence":0,"TimeStamp":"2020-04-18T20:59:42Z","LastModified":"2020-04-18T20:59:42Z"}],"Related":null,"ProcessDataExternally":null,"TotalAttributes":1,"AttributeSummaries":[],"GeneralizedType":{"$type":"4","IsComposite":false,"Parent":{"$type":"5","Key":"arousal","Numeral":8},"Key":"none","Numeral":0},"Alias":"AAAAAAAAAAAAAAAAAAAAAA==","Parent":"za+OzQvpEU6ZIEMRBhCt6A==","BaseID":"4eUOExeGL0qgj8tUPiSd2A==","ID":"NFCspbOyfkaCyfWrYAxbYA==","IndexOcurrence":1,"TimeStamp":"2020-04-18T20:59:42Z","LastModified":"2020-04-18T20:59:42Z"},{"$type":"2","Attributes":[{"$type":"3","AttributeType":"value","Description":"Attribute Type","Data":"vector","Parent":"+qdpTGaqdU2o2Ff7t7cpQg==","BaseID":"5rAfel2KoUyA3eBdeDGYwg==","ID":"Ud2E6dTOQ0OcvoEmHz3/Gg==","IndexOcurrence":0,"TimeStamp":"2020-04-18T20:59:42Z","LastModified":"2020-04-18T20:59:42Z"}],"Related":null,"ProcessDataExternally":null,"TotalAttributes":1,"AttributeSummaries":[],"GeneralizedType":{"$type":"4","IsComposite":false,"Parent":{"$type":"5","Key":"abilities","Numeral":2},"Key":"facial expression","Numeral":19},"Alias":"AAAAAAAAAAAAAAAAAAAAAA==","Parent":"za+OzQvpEU6ZIEMRBhCt6A==","BaseID":"L+wL9Ioq2k2CA7esCTm4Bg==","ID":"+qdpTGaqdU2o2Ff7t7cpQg==","IndexOcurrence":1,"TimeStamp":"2020-04-18T20:59:42Z","LastModified":"2020-04-18T20:59:42Z"},{"$type":"2","Attributes":[{"$type":"3","AttributeType":"dataObject","Description":"Attribute Type","Data":{"$type":"6","DimensionMatrix":[{"$type":"7","Dimension":1,"Label":"S1","Mid":25,"Status":true,"Maxium":50,"Minimum":0},{"$type":"7","Dimension":2,"Label":"D1","Mid":25,"Status":true,"Maxium":50,"Minimum":0},{"$type":"7","Dimension":3,"Label":"N1","Mid":25,"Status":true,"Maxium":50,"Minimum":0},{"$type":"7","Dimension":4,"Label":"O1","Mid":25,"Status":true,"Maxium":50,"Minimum":0},{"$type":"7","Dimension":5,"Label":"E1","Mid":25,"Status":true,"Maxium":50,"Minimum":0},{"$type":"7","Dimension":6,"Label":"v1","Mid":25,"Status":true,"Maxium":50,"Minimum":0}],"Title":"VectorOne","Errors":[],"HowToCompare":"isWithinRange","OperatorType":"maxPoint","Status":true},"Parent":"D/GuRD7RXEWDMn3qU5SH0Q==","BaseID":"8jZZmdF5sk6JQSBul/kaKA==","ID":"yVnw3VxOe0eRnQ77pO/Fvw==","IndexOcurrence":0,"TimeStamp":"2020-04-23T05:07:49Z","LastModified":"2020-04-23T05:07:49Z"}],"Related":null,"ProcessDataExternally":null,"TotalAttributes":1,"AttributeSummaries":[],"GeneralizedType":{"$type":"4","IsComposite":false,"Parent":{"$type":"5","Key":"emotions","Numeral":5},"Key":"tertiary","Numeral":3},"Alias":"AAAAAAAAAAAAAAAAAAAAAA==","Parent":"za+OzQvpEU6ZIEMRBhCt6A==","BaseID":"QZR6CD66aUGiTfjT9EYsTA==","ID":"D/GuRD7RXEWDMn3qU5SH0Q==","IndexOcurrence":1,"TimeStamp":"2020-04-18T20:59:42Z","LastModified":"2020-04-18T20:59:42Z"},{"$type":"2","Attributes":[{"$type":"3","AttributeType":"descriptor","Description":"Attribute Type","Data":"5887dd889ed54bceae2041aceef45fa6","Parent":"vVWQTZIRZEa6Nox62OFsDg==","BaseID":"qRIbpZyV7UOjMGoCf8BFPQ==","ID":"fRvcN7noVkyftGW4HU3oSA==","IndexOcurrence":0,"TimeStamp":"2020-04-18T20:59:42Z","LastModified":"2020-04-18T20:59:42Z"}],"Related":null,"ProcessDataExternally":null,"TotalAttributes":1,"AttributeSummaries":[],"GeneralizedType":{"$type":"4","IsComposite":false,"Parent":{"$type":"5","Key":"ontologies","Numeral":3},"Key":"subclass","Numeral":7},"Alias":"AAAAAAAAAAAAAAAAAAAAAA==","Parent":"za+OzQvpEU6ZIEMRBhCt6A==","BaseID":"PqxRI/DkTk69evubklRdEw==","ID":"vVWQTZIRZEa6Nox62OFsDg==","IndexOcurrence":1,"TimeStamp":"2020-04-18T20:59:42Z","LastModified":"2020-04-18T20:59:42Z"}],"Inherits":["EpphCOKT5E2mQLnTLlwxJQ==","FaZO7RzmyUG86mLAZrOH5A==","qyHRahx1YUWoV12e6WhYEA=="],"TotalClasses":4,"TotalMicroDescriptors":4,"TotalRootDescriptors":0,"ClassSummaries":[{"$type":"8","ClassType":{"$i":5},"Count":1},{"$type":"8","ClassType":{"$i":9},"Count":1},{"$type":"8","ClassType":{"$i":20},"Count":1},{"$type":"8","ClassType":{"$i":24},"Count":1}],"GeneralizeTypeSummaries":[{"$type":"9","GeneralizedType":{"$i":4},"Count":1},{"$type":"9","GeneralizedType":{"$i":8},"Count":1},{"$type":"9","GeneralizedType":{"$i":19},"Count":1},{"$type":"9","GeneralizedType":{"$i":23},"Count":1}],"Context":"AAAAAAAAAAAAAAAAAAAAAA==","GeneralizedType":{"$type":"4","IsComposite":false,"Parent":{"$type":"5","Key":"rootType","Numeral":0},"Key":"description","Numeral":0},"Alias":"AAAAAAAAAAAAAAAAAAAAAA==","Parent":"AAAAAAAAAAAAAAAAAAAAAA==","BaseID":"X5/PFK3kiUWhvHmcD264YQ==","ID":"za+OzQvpEU6ZIEMRBhCt6A==","IndexOcurrence":0,"TimeStamp":"2020-04-18T20:59:41Z","LastModified":"2020-04-18T20:59:41Z"}

This results in the error:

Inner Exception 1: ArgumentNullException: Value cannot be null. Parameter name: con

Any ideas?

mgholam commented 4 years ago

What is the c# code for the type?

frankinstien commented 4 years ago

I found where the problem is and the Reflection class' FastCreateInstance only creates class objects whose constructor has no parameters. My class RootDescriptor has many fields whose setters are protected but the fields' getters are public, which is why it can serialize it but can't deserialize it. Is there a means to create object with constructors that take parameters? If not there examples of how do so and might be a good addition to this library.

frankinstien commented 4 years ago

Also, ServiceStack.Text Json serializer is able to create objects whose constructors have parameters. I'm going to take a look at that source code and find out how they resolve fields to parameters since parameter names don't always match but the types do. But even if you use types as a mapping rule there are scenarios where there can be multiple same types in a constructor's parameters, so how do they resolve that?

mgholam commented 4 years ago

Try using JSONParameters.ParametricConstructorOverride = true

frankinstien commented 4 years ago

While that does allow for the class object to be created it doesn't initialize the fields correctly, whereas ServiceStack.Text does. The problem with ServiceStack.Text, for my application at least, is it doesn't Serialize deeply nested objects well. Where fastJson did serialize the objects right, however, it doesn't deserialize them right.

mgholam commented 4 years ago

Yes, properties will not be "initialized" with that workaround since the CLR just allocates the memory, this can be a problem if there is complex initialization logic, but if you are setting the values while reading the json this should not be a problem.

I have not found a fix for this since it is hard to know of the internals of an object.

One other thing to try is using JSON.FillObject() where you create the object and have fastJSON fill it for you.

frankinstien commented 4 years ago

JSON.FillObject() did not work. A quick way of doing this is to have some kind of registration of a class and map the constructor parameters to the public fields and then use the json to extract the data and pass it into the parameter list of the constructor. One could even build some kind of automation to find the Field = parameter search within the object's source code if available otherwise it's manually entered into a config or registration file.

mgholam commented 4 years ago

You could also serialize a surrogate class, instead of the original.

frankinstien commented 4 years ago

I thought of that but it would require a lot of coding for all the classes I'd need to use. I like to code once and not have to perpetually code for a chronic issue. As they say: if you're continually repeating yourself you can probably automate what you're doing or at least automate some of it...

mgholam commented 4 years ago

How about something like this:

 var o = JSON.ToObject(str, new JSONParameters { CreateParameterConstructorObject = (t) => {
        if (t == typeof(NullTestClass))
            return new NullTestClass("a", 42);
        else return null;
  }
 });
frankinstien commented 4 years ago

I still need to map the parameter to the field and extract the data from the json or am I missing something?

frankinstien commented 4 years ago

Another way to automate this is to modify the class through reflection to add a method that can accept a surrogate and dynamically create the surrogate class which is then used to deserialize the json. Where then that dynamically created surrogate is injected into the dynamically created method in the original class where a one to one map is possible to initialize the fields since they are now a part of the original class. You can then remove the dynamically created method so the class returns to its normal form. Of course, the original class to surrogate class association needs to be cached once it's created. And this is a modification to the fastJson code base.

mgholam commented 4 years ago

I still need to map the parameter to the field and extract the data from the json or am I missing something?

No the code just handles the object creation, the rest is handled by fastJSON

mgholam commented 4 years ago

From what I understand is that your problem is that there are some classes with "strange" initialization parameters that is done in the constructor, which setting the properties afterwards does not get the job done.

frankinstien commented 4 years ago

Yes, that is the problem I'm having. Some fields are not mutable but can be read.

frankinstien commented 4 years ago

The JSON.Parse does return a field value map which could be used as a pseudo surrogate. I'm going to try a few ideas. I'm going to use that collection from parsing to an external map and I'll try to emit a mapper method into the class and then remove it. I bet the external mapper is the faster approach.

mgholam commented 4 years ago

Yes, that is the problem I'm having. Some fields are not mutable but can be read.

The problem is something else, fastJSON by design does not set private/protected/internal/readonly properties or fields.

frankinstien commented 4 years ago

Yes, I know that...