function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
aebenaeben 

Don't know the type of the Apex object to deserialize in APEX JSON parsing

When JSON support was added in APEX, I was one of those guys who jumped up and down. Started using heavily for one of my integration project and everything was fine and dandy for couple week. Since yesterday, I have been noticing some weired behavior. The first time I noticed it, I thought it was one of those APEX issues I love to call "APEX weirdness" and hoped that it will fix itself (READ: getting fixed without us knowing). That hasn't happened. :(

 

Here is the issue. 

 

My JSON parsing code looks like this:

class SomeApexWrapper {

public static SomeApexWrapper getInstance(String jsonString)

JSONParser parser = JSON.createParser(jsonString);       

SomeApexWrapper w = (SomeApexWrapper) parser.readValueAs(SomeApexWrapper.class);

}

}

 

This code was working fine until two days ago. It stops working If I change any class that uses this class to parse json string. The error I get is "[Source: java.io.StringReader@21363a13; line: 1, column: 1] Don't know the type of the Apex object to deserialize"

 

Just saving the SomeApexWrapper  class again fixes the issue. 

 

Has anyone had/having this issue? Is there a permanent solution for this?

Best Answer chosen by Admin (Salesforce Developers) 
aebenaeben

Found a solution for this. This is due to a class loading issue.

 

If you look at the method listing for JSONParser class, it says that you can pass the class (type) as a parameter to readValueAs method. So you could something like,

 

JSONParser parser = JSON.createParser('JSONContent');

ApexWrapper aw = (ApexWrapper) parser.reasValueAs(ApexWrapper.class); 

 

Don't do this. There is where I had the issue. For some reason, the class ApexWrapper is not found consistently by the class loader so that it can do the deserialization.

 

The document also says that you can System.Type as a parameter. This is the best approach. So change the code as follows:

 

JSONParser parser = JSON.createParser('JSONContent');

//If you use namespace, the name needs to be qualified or use the other two param version of the forName method

Type wrapperType = Type.forName('ApexWrapper'); 

ApexWrapper aw = (ApexWrapper) parser.reasValueAs(wrapperType); 

 

What this does is, preload the class for serialization. This is the fix I had put in and so far no issues.

All Answers

joshbirkjoshbirk

"Just saving the SomeApexWrapper  class again fixes the issue."

 

So making no changes to the code, but saving/rebuilding the class fixes the issue?

aebenaeben

Thats right.

 

Have yo had this issue?

joshbirkjoshbirk

I have not, but it sounds either like something you might check with support to see if there's something odd.  It might not be JSON related, but just a compile order/related classes issue.  

 

You might try the "Compile All Classes" link on the Apex page - it seems to solve some of the odd issues sometimes.

aebenaeben

I did try that. But, I can't do it everytime I make changes in other classes. I am going to log a case with support and see what they have to say.

aebenaeben

Found a solution for this. This is due to a class loading issue.

 

If you look at the method listing for JSONParser class, it says that you can pass the class (type) as a parameter to readValueAs method. So you could something like,

 

JSONParser parser = JSON.createParser('JSONContent');

ApexWrapper aw = (ApexWrapper) parser.reasValueAs(ApexWrapper.class); 

 

Don't do this. There is where I had the issue. For some reason, the class ApexWrapper is not found consistently by the class loader so that it can do the deserialization.

 

The document also says that you can System.Type as a parameter. This is the best approach. So change the code as follows:

 

JSONParser parser = JSON.createParser('JSONContent');

//If you use namespace, the name needs to be qualified or use the other two param version of the forName method

Type wrapperType = Type.forName('ApexWrapper'); 

ApexWrapper aw = (ApexWrapper) parser.reasValueAs(wrapperType); 

 

What this does is, preload the class for serialization. This is the fix I had put in and so far no issues.

This was selected as the best answer
rungerrunger

Hmm, it's working for me either way.  If you'd post more precise instructions for getting the error, I could see about fixing it.

 

Thanks,

Kenji775Kenji775

I'd just like to say I have been encountering this error as well. It seems to be intermittent. It is happening with a custom class, that I have defined within the same class as the caller. I have tried the fix provided above, and that didn't seem to resolve it either. The line that seems to be causing the issue is when I attempt to parse some JSON received via a webservice call.

string JSONString = '{"SURVEY":32483,"STATUS":"Success","MESSAGE":"Token already created, but is still valid","TOKEN":"UUA3W6Y27MYAMGY","SUCCESS":true,"CONTACT":"0034000000l68v3AAA"}';

limeSurveyWebserviceObject limeObject = (limeSurveyWebserviceObject) JSON.deserialize(JSONString, limeSurveyWebserviceObject.class);  

global class limeSurveyWebserviceObject
{
    Integer survey;
    String status;
    String message;
    String token;
    String contact;
    Boolean success;	
}

 
Like I said this is intermitent and it could be possible it is due to bad JSON being returned by the webservice. I am attempting to add some logging to track what is returned by the webservice in event of this error so I can see for sure. I don't expect you to be able to do much with just this information, but I just wanted to make a note and say this error is still hanging around. Even if it is based on bad data from the webservice call, the error could be more helpful 

 

sgurumurthysgurumurthy

Fantastic explanation @aeben. I've been pulling my hair out since last night struggling with the same issue. I am getting JSON from a webservice call. The JSON has an Array which I am deserializing to Apex Type. The funny thing is that it would work for some time and then if I tried the same after say 30 minutes, it would give an error. I didn't realize that if I changed something in the code and tested again, it would work again. Logically explaining the symptoms was quite difficult to say the least. You have done a great job doing this. Thanks. 

 

I have changed the class loading as per the instructions below to preload the wrapper using Type.forName(). The first test went well. Will have to wait for some time to test again to ensure that it works consistently. 

sgurumurthysgurumurthy

@Kenji775, I was encoutering the same error as well and under the same conditions as well. The JSON was returned by a webservice and the error was intermittent. See @aeben's post above to preload the class type. This should help you fix the issue. 

sgurumurthysgurumurthy

I can confirm now that the fix has solved the problem and I am getting consistent results. Thanks @aeben. 

aebenaeben

@Kenji775, The following lines works for me consistently. If you are using namespaces, make sure you are passing it as the first parameter to Type.forName. Good luck.

 

Type wrapperType = Type.forName('limeSurveyWebserviceObject');

limeSurveyWebserviceObject limeObject = (limeSurveyWebserviceObject) JSON.deserialize(JSONString, wrapperType); 

TLFTLF

Thank you! Thank you! Thank you! This was making me nuts. I had code that worked in developer sandboxes but then when I deployed it to production I got this error trying to deserialize JSON in an Account object. This tip seems to work for me.

grigri9grigri9

Does anyone have consistent steps that cause this bug to happen? I.E.

 

1. change class that jsonparsing class is dependent on

2. change class that is dependent on jsonparsing class

3. run jsonparsing class

 

I tried the type.forname solution about a week ago but the bug was still intermittently occuring for me. I had to revert the production code to use the non-native parser because it worked when I deployed and then stopped working about 30 minutes later. 

 

Now I can't reproduce the bug on sandbox but I can't deploy it to production either because I have no way to confirm that the bug is gone.

Kenji775Kenji775

Sorry, no way to consistantly reproduce, but I'm hearing good things about making that wrapper class, then deserialzing to that. I'm gonna give it a shot and see what happens.

grigri9grigri9

That's what I was afraid of. I tried some of these solutions before and they didn't actually fix the issue and since I have no way to make the bug happen I have no way to confirm that it has been fixed.

Kenji775Kenji775

The error seems to be getting more prevelant and it's causing me more and more headaches. Attempting to create the wrapper class doesn't seem to help either. It seems to be random, but set at compile time. As in, sometimes when you save and reuplaod the class it works, other times it doesn't. Though it seems to be not working more than it is. Also, I'm not sure if it has to do with preloading the class, as putting the offending code in a loop with a try catch doesn't work either. It's maddeing because it tends to work in execute anonymous, but fails when in a class. It's like sometimes the main class doesn't load the custom classes defined within it. This is what my code looks like now.

limeSurveyWebserviceObject limeObject;
string parseErrorText;
integer attemptcounter = 0;

boolean parseSuccess = false;
while(true)
{
	attemptcounter++;
	if(attemptcounter > 25)
	{
		break;
	}
	try
	{
		Type wrapperType = Type.forName('limeSurveyWebserviceObject');
		limeObject = (limeSurveyWebserviceObject) JSON.deserialize(JsonString, wrapperType);
		parseSuccess = true;
		break;
	}
	catch(exception e)
	{
		system.debug('================ ERROR PARSING JSON:' + e.getMessage());
		parseErrorText = e.getMessage();
	}
} 


global class limeSurveyWebserviceObject
{
	public Integer survey{get;set;}
	public String status{get;set;}
	public String message{get;set;}
	public String token{get;set;}
	public String contact{get;set;}
	public Boolean success{get;set;}	
}

 

 

Kenji775Kenji775

Also, just for the record, the limeSurveyWebserviceObject class is defined in the same class as the code that is calling it (it's the only code that will use it, so I'm just keeping things clean).

aebenaeben

Are you using namespaces? If so, try using a fully qualified name in Type.forName.

Kenji775Kenji775

Nope, no namespaces.

Also, I'm trying your full solution

JSONParser parser = JSON.createParser('test');
Type wrapperType = Type.forName('limeSurveyWebserviceObject');
limeSurveyWebserviceObject aw = (limeSurveyWebserviceObject) parser.reasValueAs(wrapperType);

But getting the error 

 

Save error: Method does not exist or incorrect signature: [JSONParser].reasValueAs(Type) scheduler.cls /FPI Sandbox/src/classes line 197 Force.com save problem

 

I am thinking I am maybe just not quite understanding how to use your solution. I have one main class that contains both the method that is erroring and the definiton for the type of object I am trying to deserialize to. The main class is called 'scheduler'. The method that is erroring is called 'scheduleContact' and of course the object type is called 'limeSurveyWebserviceObject'. Also, the method is a global static, that is @remoteAction annotated.


Kenji775Kenji775

Wait, I might have got it. I'll post back in after my deploy.

TLFTLF

That's a compile error. There's a typo in solution... it should be "readValueAs"

Kenji775Kenji775

Nope, still epic fail. This is the current parsing setup

JSONParser parser = JSON.createParser(JSONString);
Type wrapperType = Type.forName('limeSurveyWebserviceObject'); 
limeSurveyWebserviceObject limeObject = (limeSurveyWebserviceObject) parser.readValueAs(wrapperType);	

 

 This is the JSON it is attempting to parse

{
    "SURVEY": 87628,
    "STATUS": "Success",
    "MESSAGE": "Token already created, but is still valid",
    "TOKEN": "c3cne2j6ogqyrgx",
    "SUCCESS": true,
    "CONTACT": "0034000000Ueb1R"
}

 

 

TLFTLF

Is it possible that the mapping to the fields is case sensitive? Meaning it can't map the JSON field "SURVEY" to the class field named "survey"? I know Apex is case insensitive, but perhaps the underlying reflection stuff that deals with the mapping of JSON fields to class fields is case sensitive? Just a wild guess.

aebenaeben

good point @TLF. its worth a try.

Kenji775Kenji775

Giving it a go now. Let ya know how it goes.

Kenji775Kenji775

Nope, still no good. **bleep** this bug!

rungerrunger

Sorry for the delay, this took us a great deal of effort to track down internally.  The type token (foo.class or type.forname('foo')) is the first time apex has anything remotely looking like reflection, and there was an assertion being violated in our type caching algorithm under a certain set of conditions (e.g. inheritance or inner classes) which only manifested under a certain condition that was very difficult to reproduce consistently.  Nasty one.

 

The bugfix should be rolled out in the next few days.

Kenji775Kenji775

I hardly know what any of that means, but I saw the words 'fixed' and 'next few days' which means I love you XD Thank you so much, (both you and your team) for tracking this down and finding what will hopefully be a fix.

kibitzerkibitzer

I've been running into the same issue deserializing the standard CampaignMember object. Do you think the fix will resolve that issue as well?

 

Also, did you know that deserializaiton fails for sObjects that contain custom date fields? (can't deserialize date format YYYY-MM-DD)


Dan

grigri9grigri9

I'm REALLY curious because I spent quite a bit of time trying to reproduce this consistently (unsuccesfully). What was the certain condition?

rungerrunger

kibitzer wrote:

I've been running into the same issue deserializing the standard CampaignMember object. Do you think the fix will resolve that issue as well?

 

Also, did you know that deserializaiton fails for sObjects that contain custom date fields? (can't deserialize date format YYYY-MM-DD)


Dan


Can you provide specific code samples that fail for you?  I'll check into it.

rungerrunger

grigri9 wrote:

I'm REALLY curious because I spent quite a bit of time trying to reproduce this consistently (unsuccesfully). What was the certain condition?


It would be impossible to reproduce it consistently, as it has to do with the order things are evicted from the cache we use for apex class definitions.  It's a two-level cache and the semantics are pretty complicated.

kibitzerkibitzer

Here's some deserialization test code that fails - but not on every org!

 

Create a custom 'Date' field TestDateField on the CampaignMember object. Then run the following test class.

 

 

@istest
public class serializationtest
{

public static testmethod void testserialization()
{
Lead ld = new Lead(company='comp',lastname='last');
Campaign camp = new Campaign(Name='camp',IsActive=true);
insert ld;
insert camp;
CampaignMember cm = new CampaignMember(LeadID = ld.id, CampaignID = camp.id, Status='Responded');
cm.TestDateField__c = Date.Today();
insert cm;
String ser = json.serialize(cm);
System.debug(ser);
CampaignMember cmrestored = (CampaignMember)json.deserialize(ser, CampaignMember.class);
}

}

 

The exception you get is: System.JSONException: Cannot deserialize instance of date from VALUE_STRING value 2011-11-30 at [Source: java.io.StringReader@215a9da7; line: 1, column: 263]

 

It's case 06644992 on the partner portal (just opened yesterday before I found this thread). The support rep told me that it worked if you changed Date.Today() to DateTime.Now().Date() - and it did, on his org. But it still failed on mine - which he found quite surprising. I have two orgs that I know of where it fails.

 

There is a workaround - use regular expressions to replace the date string in the serialized data with the numeric Unix timestamp and it works.The problem does not occur with serialized APEX classes. I haven't tried it with SObjects other than CampaignMember.

 

Hopefully this is enough information for you to puzzle out the issue.

 

Enjoy :-)

 

Dan


runger wrote:

kibitzer wrote:

I've been running into the same issue deserializing the standard CampaignMember object. Do you think the fix will resolve that issue as well?

 

Also, did you know that deserializaiton fails for sObjects that contain custom date fields? (can't deserialize date format YYYY-MM-DD)


Dan


Can you provide specific code samples that fail for you?  I'll check into it.




rungerrunger

Thanks, that reproduces the problem for me.  I should be able to get this fix into the upcoming release.

tom_patrostom_patros

Based on your previous note about the caching, it looks like if you re-save your Apex class with the @remoteAction that is misbehaving, it appears to correct the issue. Not a solution for anyone, I'm sure, but at least you can keep working.

 

Rich: when you say "upcoming release", is that Spring '12 or (hopefully) before that?

rungerrunger

I mean Spring '12.  It's checked into the Spring '12 code line now.

tom_patrostom_patros

So, if Spring '12 is typically around Februrary, this suggests that Apex JSON and/or remoteActions are not production-ready until then? 

 

Recompiling / redeploying an Apex class to clear it from the cache isn't going to be a viable option for production apps.

rungerrunger

I share your frustration.  We're in a change moratorium for production at the moment, there's nothing I can do.

This particular bug only appears to manifest when dealing with inheritance or inner classes.  If you flatten out the class hierarchies that you're deserializing, you should be able to avoid it.

Kenji775Kenji775

It's alright, at least I know there is a fix being worked on.

 

Being as I suck at programming in general, could you explain that workaround a little more, or give some sample code to explain what you mean? Do I just take my custom class and put in it's own file?

tom_patrostom_patros

Thanks Rich - my issue is indeed occuring related to an inner class. I'm going to try to flatten it out and try again.

jon-wujon-wu

Did that fix your issue? I had 2 inner classes and I moved them into their own files and I thought I fixed it but the problem is back again.

 

However, I took out the Type.forName() code. Should that still be required?

 

I'm just wondering what I need to do to make my code work until Spring.

 

 

JarrettKJarrettK

Thanks for getting an answer on this thread, helped us get our apex correct and functional with very little run around.  Definately likes to act up upon remoting.

tom_patrostom_patros

I removed my inner classes but am still getting the same error.

LearnerSFLearnerSF

I'm glad I'm not the only person that's seeing this.  I'm trying to deserialize into a instance of a class that contains an array of instances of another class.  I'm only using Strings and Integers, and there is no class nesting.

 

The only difference between the sample code below and my real code is the # of strings and the variable and class names.  The rest is pretty much verbatim.

 

This problem is a blocker for our release of a feature... we were planning on using the JSON API to facilitate communication with a web service.

 

==== file = "Inner.cls" =======

public class Inner {

public String s1;

public String s2;

 

public Inner(String s1, String s2) {

this.s1 = s1;

this.s2 = s2; 

}

}

 

==== file = "Outer.cls" =======

public class Outer {

public Integer numResults;

public Inner[] results;

 

public Outer(Integer numResults, Inner[] results) {

this.numResults = numResults;

this.results = results;

}

 

public static testmethod void testDeserialize() {

// create JSON which represents

// Outer containing an array of two Inner instances

String jsonToParse = '{"numResults" : 2, "results" : [';
jsonToParse = jsonToParse + '{"s1": "a0550000008Wit0",';
jsonToParse = jsonToParse + '"s2": "something"},';
jsonToParse = jsonToParse + '{"s1": "a0550A0F008Zbd0",';
jsonToParse = jsonToParse + '"s2": "something else"}]}';

Outer response = (Outer) JSON.deserialize(jsonToParse, Outer.class);

}

}

 

Chris Merrill

Senior Software Engineer

Adconion Media Group

cmerrill@adconion.com

 

rungerrunger

LearnerSF, what's your org id?

JarrettKJarrettK

Is there a solid work around, or is this bug going to be patched before Spring 12?  We change our class structure and it will start working, then it will stop again.  Do I need to choose an alternative to deserializing until the Spring 12 release?

rungerrunger

Well, until this is fixed you can always use the JSONParser class to read the instance variables instead of deserialize/readValueAs.

tom_patrostom_patros

I've been wrestling with this the past few days. Some things I've noticed:

 

  • This issue does not appear to be limited to inner classes. I moved some inner classes out to their own top-level classes and got the same error.
     
  • This isssue occurs when deserializing to standard and custom objects (ie Account, MyObject__c), not just custom classes. I was initially having this issue on a "AccountWrapper" class. I changed the code to use an Account directly and got the same error when deserializing.

  • It doesn't seem to have anything to do with what fields / data types are at play. At first I thought it was just date/datetime fields.

  • As Rich alluded to, there is definitely a duration of time when everything works, and at some point in time it stops, which sounds a lot like a cache that's being cleared after a period of time or a period of non-use of that class.
     
  • Related to the previous bullet, I find that if I re-save the class, it starts working again. I just can't determine how long it will work for.

It's definitely a challenge for a current project I'm working on, as we are relying on serialization / deserialization to make parameter passing as abstract as possible. I guess parsing is an acceptable fallback, we just didn't factor that into the timing to write, test and deliver the app, and it introduces a lot of baggage.

 

I'm currently testing one last-ditch idea: if I have a VF page that uses my class as its controller and I somehow ping that VF page on a frequent interval (every 5 minutes), will that keep the class "alive" in the cache?

kibitzerkibitzer

I am experiencing the exact same issue attempting ot deserialize CampaignMember objects.

 

I've also noticed that the odds of seeing a failure increase when the code that does the deserialization is invoked from a unit test class that contains multiple tests, and it's run under the Apex test runner.


But the problem is very intermittent.

 

I do know that when the failure occurs, the type information from CampaignMember.class is not value (displays as aot=null in a debug message).

 

Unfortunately, I have not been able to come up with sample code that reliably reproduces the error.

rungerrunger

Okay, I'm pretty sure I've got a final solution to this, and it will be available in the upcoming major release (Spring '12).  In the meantime, it appears to not be an issue in the new bytecode runtime.  Not much solace to people developing packages for the appexchange, i know, but if you're running into this issue and you want to flip your org to the new runtime, please contact support.

Mr FordMr Ford

Hey Rich,

 

I found this thread while searching for "Don't know the type of the Apex object to deserialize at", and it seems that I am encountering the same problem as the other folks here.

 

I have nested wrapper classes and I am trying to use JSON.deserialize to put a webservice's response into the classes.  (The deserialization is done in a VF controller, for what it's worth.)

 

I'm getting basically the same behaviour as everyone else is describing - it works 80% of the time, then stops working and throwing the "Don't know the type" exception.

 

One thing I have not been able to do is resolve the problem by recompiling code.  I have observed the following though: when I get the error on my VF page in Chrome, if I change to Firefox and load my VF page it works first time and works if I refresh the page in Chrome.  (For the record, I do a bunch of refreshes, saving code, screaming and crying before I switch to Firefox.)

 

Does this make any sense in the context of the problem you are seeing?  Does the request coming from a different browser cause the Apex wrapper classes to be reloaded, whereas repeated sending of the request from one browser does not?

 

Thanks in advance,

Trevor Ford.

kibitzerkibitzer

I was doing the deserializing inside of a trigger - so I don't know that Visual Force would have any impact.

rungerrunger

The server's behavior is identical in this regard irrespective of browser type.  I think you're just seeing a correlation due to chance.

Mr FordMr Ford

Thanks for confirming that Rich.

 

I haven't seen the error since the bytecode runtime was switched in our sandbox, thanks a lot for that tip!

 

Cheers,

Trevor.