I need to consume JSON from a REST service. For one of the fields, the service sometimes returns a string for the value yet other times returns an array of strings for the value.
For example:
var jsonArrayResponse = "{ \"id\" : \"abc\", \"relatedIds\" : [ \"def\", \"ghi\", \"jkl\" ] }";
var jsonSingleResponse = "{ \"id\" : \"123\", \"relatedIds\" : \"456\" }";
I have implemented the destination class using the SingleValueArrayConverter pattern found on this post. Here is the full set of code to illustrate my problem:
class Program
{
static void Main(string[] args)
{
// this works fine with the SingleValueArrayConverter
var jsonArray = "{ \"id\" : \"abc\", \"relatedIds\" : [ \"def\", \"ghi\", \"jkl\" ] }";
var objArray = (MyObject)JsonConvert.DeserializeObject(jsonArray, typeof(MyObject));
// this fails to parse "456" with Exception message:
// "Unable to cast object of type 'System.Object' to
// type 'System.Collections.Generic.List`1[System.String]'."
var jsonSingle = "{ \"id\" : \"123\", \"relatedIds\" : \"456\" }";
var objSingle = (MyObject)JsonConvert.DeserializeObject(jsonSingle, typeof (MyObject));
}
}
[DataContract]
public class MyObject
{
[DataMember(Name = "id")]
public string Id;
[DataMember(Name = "relatedIds")]
[JsonConverter(typeof(SingleValueArrayConverter<string>))]
public List<string> RelatedIds;
}
public class SingleValueArrayConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal = new Object();
if (reader.TokenType == JsonToken.StartObject)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T>() { instance };
}
else if (reader.TokenType == JsonToken.StartArray)
{
retVal = serializer.Deserialize(reader, objectType);
}
return retVal;
}
public override bool CanConvert(Type objectType)
{
return false;
}
}
How can I fix this to properly parse the two different JSON blobs into the same class?
UPDATE:
I modified the SingleValueArrayConverter to check the string case (as mentioned below), and things started working. I use the converter for more than just strings, so I had to add the first if statement, rather than modify it as suggested).
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal = new Object();
if (reader.TokenType == JsonToken.String)
{
string instance = (string)serializer.Deserialize(reader);
retVal = new List<string> () {instance};
}
else if (reader.TokenType == JsonToken.StartObject)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T>() { instance };
}
else if (reader.TokenType == JsonToken.StartArray)
{
retVal = serializer.Deserialize(reader, objectType);
}
return retVal;
}
public override bool CanConvert(Type objectType)
{
return false;
}
}