Rational for Error: JSON does not encoded the data types of the data, thus you have to give your class type to allow it to understand how to parse the data as it works its way through the class members. The Object data type is not enough for the deserialize method to know the data type.
Using JSON.deserializeUntyped. To work around this you can manually parse the JSON using the various JSON parser methods or use the deserializeUntyped method which returns conveniently a map that contains primitive data types!
So I've leveraged this in some new methods to encapsulate this with your Parameters class.
public class Parameters{
private Map<String, Object> parameters = new Map<String, Object>();
public void add(String name, Object value) {
this.parameters.put(name, value);
}
public Object get(String name) {
Object result = null;
if(this.parameters.containsKey(name)) {
result = this.parameters.get(name);
}
return result;
}
public String serialize()
{
return JSON.serialize(parameters);
}
public static Parameters deserialize(String serialized)
{
Parameters parameters = new Parameters();
parameters.parameters = (Map<String, Object>) JSON.deserializeUntyped(serialized);
return parameters;
}
}
This is a slightly modified version of your test code to use the new helper methods.
Parameters params = new Parameters();
params.add('Name', 'Robert');
params.add('Age', 36);
String serialized = params.serialize();
Parameters deserialized = Parameters.deserialize(serialized);
System.assertEquals(params.toString(), deserialized.toString());
I've used toString to compare equality, as without it the check fails, as the two variables point to different instances of the same object.
Alternative:
This naked version also works as well...
Map<String, Object> parameters = new Map<String, Object>();
parameters.put('A', 36);
parameters.put('B', 'Robots');
String serialized = JSON.serialize(parameters);
System.debug(serialized);
parameters = (Map<String, Object>) JSON.deserializeUntyped(serialized);
System.debug(parameters.get('B'));
If you know the structure of the JSON before you get it then the easiest way is to create a class to define the Object.
For Example, given the following JSON String
{
"id" : "someID",
"street" : "someStreet",
"listOfClass" : [
{
"myVar" : "test"
}
]
"myArray" : ["1","2","3"]
}
Define the object as such:
public class myJSONClass{
public string id;
public string street;
public listOfClass[] listOfClass;
public String[] myArray;
public class listOfClasses{
public String myVar;
}
}
Do not think of it as a class to clutter your other classes, think of it as an object definition (what it is)
Then simply deserialize as follows:
myJSONClass obj = (myJSONClass)JSON.deserialize(JSONSTRING,myJSONClass.class);
Where it becomes tricky is when you are using reserved names or an unknown structure.
In most of those cases you need to use the JSON Parser methods to parse the string.The exception is if you only have a FEW reserved names you can replace them as such
JSONString = JSONString.replace('name','name_Z');
then define the property in the class as name_Z
. It will save a lot of work.
Back to your original point, if you have no class you can deserialize as such:
Map<String,Object> obj = JSON.deserializeUntyped(JSONString);
Then you can just cast everything to strings:
Map<String,String> results = New Map<String,String>();
for(String key : obj.keySet()){
results.put(key,string.valueOf(obj.get(key));
}
However in this case you lose the structure. For example, in your string if you have lists, arrays, etc, you get the string representation of the values and not the actual structure. More useful to define a class and work with it.
Note You do not have to have everything defined in the class to deserialize unless you use deserializeStrict. So if in my first example you removed public String id
it would still deserialize
In all the above examples, make sure to check for nulls and errors
Best Answer
Future method parameters are limited to primitives and collections of primitives. You're attempting to pass in a collection of collections, which is not supported.
You're correct that you can use JSON to get around this. This should put you on the right track:
A more detailed example can be found here: Passing Objects to Future Annotated Methods.
Also, please take a look at this question regarding the potential implications of using this technique.