Parse an array object without an array as an array using Json.net

I work with an external API that returns a property either as an array or as an object, depending on the quantity. What is a good way to handle this?

Return as an array:

{ "contacts": { "address": [ { "id": "47602070", "type": "Work", "street": "MyStreet", "city": "MyCity", "zip": "12345", "country": "USA" }, { "id": "47732816", "type": "GPS", "street": "50.0,30.0" } ] } } 

Return as object:

 { "contacts": { "address": { "id": "47602070", "type": "Work", "street": "MyStreet", "city": "MyCity", "zip": "12345", "country": "USA" } } } 

I think a workaround would be to use my own deserializer and return an array of length 1 for the object case and default deserialization for the array, but I don’t know yet how to do this.

I tried to deserialize the object into an array and hoped that Json.net would handle this case for me, but not the cube.

+6
source share
3 answers

A custom JSON.NET converter can do the trick here. It is not that difficult.

For the DateTime property, you can do this as follows. Just format the corresponding property using a custom converter.

 [JsonObject(MemberSerialization.OptIn)] public class MyClass { [JsonProperty(PropertyName = "creation_date")] [JsonConverter(typeof(UnixDateTimeConverter))] public DateTime CreationDate { get; set; } } 

JSON.NET provides most of the plumbing. Just get from the base converter.

 public class UnixDateTimeConverter : DateTimeConverterBase { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { ...} public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { ... } } 

All you have to do is implement the ReadJson (deserialization) and WriteJson (serialization) methods.

Here you can find the full example:

Writing a Custom Json.NET DateTime Converter

For your specific problem you need a little more control. Try the following type of converter:

 public class Contact { private List<Address> _addresses = new List<Address>(); public IEnumerable<Address> Addresses { get { return _addresses; } } public class ContactConverter : CustomCreationConverter<Contact> { public override Contact Create(Type objectType) { return new Contact(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var mappedObj = new Contact(); // Parse JSON data here // ... return mappedObj; } } 

Using your own converter, similar to the one above, you can analyze the JSON data yourself and make contact objects, as you wish.

I changed the example I found here:

Custom JSON.NET Converters - Quick View

In this case, you need to pass the custom converter during deserialization.

 Contact contact = JsonConvert.DeserializeObject<Contact>(json, new ContactConverter()); 
+3
source

Based on Christoph Gears answer, here is what I ended up doing.

  • Create a custom JSON converter to always parse JSON as an array. If JSON is an object without an array, then deserialize the object and wrap it in an array.

  • Mark the appropriate properties using the custom converter attribute.

Custom converter

 public class JsonToArrayConverter<T> : CustomCreationConverter<T[]> { public override T[] Create(Type objectType) { // Default value is an empty array. return new T[0]; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.StartArray) { // JSON object was an array, so just deserialize it as usual. object result = serializer.Deserialize(reader, objectType); return result; } else { // JSON object was not an arry, so deserialize the object // and wrap it in an array. var resultObject = serializer.Deserialize<T>(reader); return new T[] {resultObject}; } } } 

Data structures for example question

 public class Organisation { public Contacts contacts; } public class Address { public string id; public string street; public string city; public string type; public string zip; public string country; } public class Contacts { // Tell JSON.net to use the custom converter for this property. [JsonConverter(typeof(JsonToArrayConverter<Address>))] public Address[] address; } 
+5
source

Note. Instead of using CustomCreationConverter you can just use a regular converter. For example, I use something like this:

 public class SingleToArrayConverter<T> : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var items = (IEnumerable<T>)value; if (value == null || !items.Any()) { writer.WriteNull(); } else if (items.Count() == 1) { serializer.Serialize(writer, items.ElementAt(0)); } else { serializer.Serialize(writer, items); } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (!CanConvert(objectType)) { throw new NotSupportedException(); } if (reader.TokenType == JsonToken.Null) { reader.Skip(); return null; } else if (reader.TokenType == JsonToken.StartObject) { return new T[] { serializer.Deserialize<T>(reader) }; } else { return serializer.Deserialize<T[]>(reader); } } public override bool CanConvert(Type objectType) { return objectType == typeof(IEnumerable<T>); } } 
+1
source

Source: https://habr.com/ru/post/917795/


All Articles