Deserializing JSON When Sometimes Array and Sometimes Object

Deserializing JSON when sometimes array and sometimes object

The developer of JSON.NET ended up helping on the projects codeplex site. Here is the solution:

The problem was, when it was a JSON object, I wasn't reading past the attribute. Here is the correct code:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<List<FacebookMedia>>(reader);
}
else
{
FacebookMedia media = serializer.Deserialize<FacebookMedia>(reader);
return new List<FacebookMedia>(new[] {media});
}
}

James was also kind enough to provide unit tests for the above method.

Error Deserializing JSON Object/Array using newtonsoft

Look at Camilo Martinez answer on this discussion :
Deserializing JSON when sometimes array and sometimes object

Basically, you'll need to add the JsonConverter attribute to your BookingInfo property and handle the conversion in a JsonConverter implementation.

public class FlightOptionsList
{
public List<FlightOption> FlightOption { get; set; }
}

public class FlightOption
{
public string LegRef { get; set; }
public string Destination { get; set; }
public string Origin { get; set; }
public Option Option { get; set; }
}

public class Option
{
public string Key { get; set; }
public string TravelTime { get; set; }
[JsonConverter(typeof(SingleValueArrayConverter<BookingInfo>))]
public List<BookingInfo> BookingInfo { get; set; }
public Connection Connection { get; set; }
}

public class BookingInfo
{
public string BookingCode { get; set; }
public string BookingCount { get; set; }
public string CabinClass { get; set; }
public string FareInfoRef { get; set; }
public string SegmentRef { get; set; }
}

public class Connection
{
public string SegmentIndex { get; set; }
}

And here's the converter :

public class SingleValueArrayConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}

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 true;
}
}

Deserialize json where properties have an object instead of an array when there would be only one entry?

Shortly after posting this I found a blogpost further down in the search results that solves this exact problem:

http://michaelcummings.net/mathoms/using-a-custom-jsonconverter-to-fix-bad-json-results/

edit 2017-07-19: Another blogpost popped up: http://trycatchfail.com/blog/post/Dealing-with-Horrid-No-Good-Very-Bad-APIs-Using-JSONNET

In case it ever goes down, here is the relevant code for a JsonConverter:

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;
}

edit:

What I ended up doing was adding an interface:

public interface IHasContent
{
string Content { get; set; }
}

as at least the string only types always had the same name for those.

the problematic types got the attribute and interface:

[JsonConverter(typeof(SingleValueArrayConverter<ShippingInfo>))]
public class ShippingInfo : IHasContent

For the string conversion I added a simple implicit operator:

public static implicit operator ShippingInfo(string s)
{
return new ShippingInfo { Content = s };
}

The converter got some type constraints:

public class SingleValueArrayConverter<T> : JsonConverter where T : IHasContent, new()

and finally, here is the ReadJson method:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal;
switch (reader.TokenType)
{
case JsonToken.StartObject:
var instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T> { instance };
break;

case JsonToken.StartArray:
retVal = serializer.Deserialize(reader, objectType);
break;

case JsonToken.String:
retVal = ReadStringAsContentObject();
break;

default:
throw new ArgumentException();
}
return retVal;
object ReadStringAsContentObject()
{
var content = new T();
content.Content = reader.ReadAsString();
var returnObject = new List<T> { content };
return returnObject;
}
}

Deserialize JSON as object or array with JSON.Net

You can change the property type for "data" in your model to dynamic or an object and check if it is an array on run-time.

Here's an example:

public class Response
{
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("data")]
public dynamic Data { get; set; }
}


var response = JsonConvert.DeserializeJson<Response>(json);
.
.
.

Type responseDataType = response.Data.GetType();

if(responseDataType.IsArray) {
// It's an array, what to do?
}
else {
// Not an array, what's next?
}

Deserialize JSON, sometimes value is an array, sometimes (blank string)

Yes.

Inside the JsonConverter test the token type from the JsonReader.

If it is a string then return null.

If it is the start of an array then use the JsonReader and JsonSerializer passed to the converter method to deserialize the array.



Related Topics



Leave a reply



Submit