TL;DR
If you have the problem
No parameterless constructor defined for type of ‘System.String’!
just add the DeserializeObject and InterceptError functions , add this 2 lines:
var obj = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json); //json parameter is the string content of the api
<your object> = DeserializeObject(obj, typeof(<your object type>)) as <your object type>;
watch the debug window and you will see the problems marked with !!!
( See also video at http://youtu.be/7oZ37pnSNtM)
Long story:
I wanted to do a tutorial for obtaining data from foursquare from Web , Desktop and mobile applications. I have made some research first and I have discovered the API and a library for obtaining data.
Unfortunately, foursquare changed his API and , when deserializing, the application was giving
“No parameterless constructor defined for type of ‘System.String’.
at this line
fourSquareResponse = new JavaScriptSerializer().Deserialize<FourSquareSingleResponse<T>>(json);
I have made a custom code to see where the errors are.
I do not want to explain the code, just look at it and then I will show how to use:
/// <summary>
/// TODO: make this add to global errors to return to the caller
/// </summary>
/// <param name="message"></param>
private void InterceptError(string message)
{
var cc = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ForegroundColor = cc;
Debug.WriteLine(message);
}
private object DeserializeObject(Dictionary<string, object> dictItem, Type tip)
{
var newInstance = Activator.CreateInstance(tip);
var isDictionary = (tip.GetInterface("IDictionary") != null);
PropertyInfo p = null;
foreach (var k in dictItem.Keys)
{
var ser= new JavaScriptSerializer().Serialize(dictItem[k]);
Type tipP = null;
if (isDictionary)
{
tipP = tip.GetGenericArguments()[1];
}
else
{
try
{
p = tip.GetProperty(k);
if (p == null)
throw new ArgumentNullException(k);
}
catch (Exception)
{
//Console.WriteLine(ex.Message);
InterceptError("missing property:" + k + " to class" + tip.FullName + " value:" + ser);
continue;
}
tipP = p.PropertyType;
}
if (tipP.IsClass && tipP.FullName != typeof(string).FullName)
{
dynamic val = null;
try
{
val = new JavaScriptSerializer().Deserialize(ser, tipP);
}
catch (Exception ex)
{
Console.WriteLine("error for class:" + k + ex.Message);
IList arr = dictItem[k] as object[];
if (arr == null)
{
arr = dictItem[k] as ArrayList;
}
if (arr == null)
{
var t1 = dictItem[k] as Dictionary<string, object>;
if (t1 == null)
{
InterceptError("Not a dictionary, not an array - please contact ignatandrei@yahoo.com for " + k);
}
val = DeserializeObject(dictItem[k] as Dictionary<string, object>, tipP);
}
else
{
val = Activator.CreateInstance(tipP);
var tipGen=tipP.GetGenericArguments()[0];
foreach (var obj in arr)
{
val.Add((dynamic)DeserializeObject(obj as Dictionary<string, object>, tipGen));
}
}
}
if (isDictionary)
{
((IDictionary)newInstance).Add(k, Convert.ChangeType(val, tipP));
}
else
{
p.SetValue(newInstance, Convert.ChangeType(val, tipP), null);
}
}
else//simple int , string,
{
try
{
if (isDictionary)
{
((IDictionary)newInstance).Add(k, Convert.ChangeType(dictItem[k], tipP));
}
else
{
p.SetValue(newInstance, Convert.ChangeType(dictItem[k], tipP), null);
}
}
catch (Exception ex)
{
InterceptError("!!!not a simple property " + k + " from " + tip + " value:" + ser);
}
}
}
return newInstance;
}
Now the original code that throws the error looks this way:
try
{
fourSquareResponse = new JavaScriptSerializer().Deserialize<FourSquareSingleResponse<T>>(json); //json parameter is the string content of the api
}
catch (Exception ex)
{
var obj = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json); //json parameter is the string content of the api
fourSquareResponse = DeserializeObject(obj, typeof(FourSquareSingleResponse<T>)) as FourSquareSingleResponse<T>;
}
When looking to the debug window, you will see:
!!!not a simple property icon from FourSquare.SharpSquare.Entities.Category value:{"prefix":"https://ss1.4sqi.net/img/categories_v2/building/home_","suffix":".png"}
If looking to the source code, you will see
public string icon
{
get;
set;
}
And in the foursquare api you will see
- icon: {
- prefix: "https://ss1.4sqi.net/img/categories_v2/travel/trainstation_"
- suffix: ".png"
}
Now it is clear : icon is not a string, is a class!
So just add a class with required properties , modify the property from string to this class and it is all solved!
( for me, the class is:
public class Icon
{
public string prefix { get; set; }
public string suffix { get; set; }
}
and the definition will be:
public Icon icon
{
get;
set;
}
Moral of the story:
When obtaining data via HTTP API, just be sure that you can handle modifications!