On some occasions when I receive JSON whose one of the array property is empty the deserialization fails, throwing the following exception :
Cannot deserialize the current JSON object (eg {"name":"value"}) into type 'SonicApi.ClickMark[]' because the type requires a JSON array (eg [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (eg [1,2,3]) or change the deserialized type so that it is a normal .NET type (eg not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path auftakt_result.click_marks, line 1, position 121.
Trying to ignore null values with the following code didn't help:
var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore;
Here is an example of JSON that produces the error :
{
"status": {
"code": 200
},
"auftakt_result": {
"clicks_per_bar": 0,
"overall_tempo": 0,
"overall_tempo_straight": 0,
"click_marks": {}
}
}
Here is an example of JSON whose array is not empty and does not produce any error:
{
"status": {
"code": 200
},
"auftakt_result": {
"clicks_per_bar": 8,
"overall_tempo": 144.886978,
"overall_tempo_straight": 144.90889,
"click_marks": [
{
"index": 0,
"bpm": 144.226624,
"probability": 0.828170717,
"time": 0.0787981859,
"downbeat": "false"
},
{
"index": 1,
"bpm": 144.226517,
"probability": 0.831781149,
"time": 0.286802721,
"downbeat": "false"
},
etc ...
Here are the C# types representing above objects:
public sealed class AnalyzeTempoResponse
{
[JsonProperty("auftakt_result")]
public AuftaktResult AuftaktResult { get; set; }
[JsonProperty("status")]
public Status Status { get; set; }
}
public sealed class Status
{
[JsonProperty("code")]
public int Code { get; set; }
}
public sealed class AuftaktResult
{
[JsonProperty("clicks_per_bar")]
public int ClicksPerBar { get; set; }
[JsonProperty("overall_tempo")]
public double OverallTempo { get; set; }
[JsonProperty("overall_tempo_straight")]
public double OverallTempoStraight { get; set; }
[JsonProperty("click_marks")]
public ClickMark[] ClickMarks { get; set; }
}
public sealed class ClickMark
{
[JsonProperty("index")]
public int Index { get; set; }
[JsonProperty("bpm")]
public double Bpm { get; set; }
[JsonProperty("probability")]
public double Probability { get; set; }
[JsonProperty("time")]
public double Time { get; set; }
[JsonProperty("downbeat")]
public string Downbeat { get; set; }
}
How can I deserialize responses whose click_marks
content is null ?
If that matters, I am using the latest version of Newtonsoft.Json : v6.0
EDIT
Here is the adopted solution according @khellang's answer :
public class ClickMarkArrayConverter : CustomCreationConverter<ClickMark[]>
{
public override ClickMark[] Create(Type objectType)
{
return new ClickMark[] {};
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize(reader, objectType);
}
if (reader.TokenType == JsonToken.StartObject)
{
serializer.Deserialize(reader); // NOTE : value must be consumed otherwise an exception will be thrown
return null;
}
throw new NotSupportedException("Should not occur, check JSON for a new type of malformed syntax");
}
}
It has nothing to do with null
values (none of your JSON examples have null
values for any property). You're trying to deserialize a JSON object into a ClickMark[]
:
"click_marks": {} // <-- This is an object, not null, not an array.
The reason it works for example number two is that the click_marks
property actually is an array of ClickMark
objects:
"click_marks": [{...}, {...}, {...}] // <-- This is an array with three objects.
Where does the data come from? You need to make sure that the click_marks
property is either an array or an object, not both, and that your typed C# object ClickMarks
matches the "type" of the JSON property.
If you have no control over the data, eg if it comes from a 3rd party, I'd suggest you write a custom JsonConverter
that you can apply to that single property:
public class ObjectToArrayConverter<T> : CustomCreationConverter<T[]>
{
public override T[] Create(Type objectType)
{
return new T[0];
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize(reader, objectType);
}
else
{
return new T[] { serializer.Deserialize<T>(reader) };
}
}
}
And apply it like this:
public sealed class AuftaktResult
{
// ...
[JsonProperty("click_marks")]
[JsonConverter(typeof(ObjectToArrayConverter<ClickMark>))]
public ClickMark[] ClickMarks { get; set; }
}
This will check if the value is a single object and wrap it in an array so it will match your C# POCO property :)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.