DavidWaldo
DavidWaldo

Reputation: 735

irregular Json content

Right now I'm solving a problem, Ive never run into. I have an ASP.Net rest API with this class in my models:

public class Filter<T> where T : class
{
    [JsonProperty(PropertyName = "property1", Required = Required.Always)]
    public string Property1 { get; set; }

    [JsonProperty(PropertyName = "property2", Required = Required.Always)]
    public string Property2 { get; set; } = null;

    [JsonProperty(PropertyName = "property3", Required = Required.Always)]
    public string Property3 { get; set; }

However client needs to send me in one of the calls Array like this:

    "filter": [
    {
        "property1": "xxxx",
        "property2": "xxxx",
        "property3": "xxxx"
    },
    {
        "property1": "xxxx",
        "property2": ["xxxx","yyyy"],
        "property3": "xxxx"
    }
],

Problem is property 2, which is string once and array another time. How can I deal with this type of problem? Client wont budge. Property is alwayes named as a property2.

Upvotes: 1

Views: 100

Answers (2)

VDWWD
VDWWD

Reputation: 35544

You can use a JsonConverter for this. The converter will check if it is an array and if yes return it. If not create one and add the only value as the first object in that array.

internal class MyConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<T>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken jtoken = JToken.Load(reader);

        if (jtoken.Type == JTokenType.Array)
        {
            return jtoken.ToObject<List<T>>();
        }
        else
        {
            return new List<T> { jtoken.ToObject<T>() };
        }
    }

    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then change Property2 to a List<string> and add the Converter as a property.

[JsonConverter(typeof(MyConverter<string>))]
[JsonProperty(PropertyName = "property2", Required = Required.Always)]
public List<string> Property2 { get; set; }

The test

string json_with_array = "[ { \"property1\": \"xxxx\", \"property2\": \"xxxx\", \"property3\": \"xxxx\" }, { \"property1\": \"xxxx\", \"property2\": [\"xxxx\",\"yyyy\"], \"property3\": \"xxxx\" } ]";
string json_without_array = "[ { \"property1\": \"xxxx\", \"property2\": \"xxxx\", \"property3\": \"xxxx\" }, { \"property1\": \"xxxx\", \"property2\": \"zzzz\", \"property3\": \"xxxx\" } ]";

List<TestClass> list1 = JsonConvert.DeserializeObject<List<TestClass>>(json_with_array);
List<TestClass> list2 = JsonConvert.DeserializeObject<List<TestClass>>(json_without_array);

List<TestClass> merged_list = list1.Concat(list2).ToList();

string merged_json = JsonConvert.SerializeObject(merged_list);

The result

[
  {
    "property1": "xxxx",
    "property2": [
      "xxxx"
    ],
    "property3": "xxxx"
  },
  {
    "property1": "xxxx",
    "property2": [
      "xxxx",
      "yyyy"
    ],
    "property3": "xxxx"
  },
  {
    "property1": "xxxx",
    "property2": [
      "xxxx"
    ],
    "property3": "xxxx"
  },
  {
    "property1": "xxxx",
    "property2": [
      "zzzz"
    ],
    "property3": "xxxx"
  }
]

Upvotes: 2

Marfee
Marfee

Reputation: 169

You can make some kind of mediator class between your JSON call and Filter class in which you will check for property type instead of instantly mapping it.

Then, change type of your Property2 to array instead of string and check length of your property while using class in order to check if it's a single string or array.

Edit: assuming your array is array of strings. If not, do the same with a general class like object.

Upvotes: 1

Related Questions