GPGVM
GPGVM

Reputation: 5619

Check JObject contains an array

The business rules are simple. We have a method that takes a JObject as a parm. Convert it to a c# poco.

The json needs to represent a single object.

No arrays allowed. If you need to do it three times call the method three times.

So for example this would be valid json:

{
   "CustomerId": 669616948,
   "FirstName": "ERIC",
   "LastName": "TEST2",
   "BirthYear": 0,
   "BirthMonth": 0,
   "CustomerState": 0,
   "LegalAddressState": null,
   "Username": "ERIC2222"
}

this would not:

{
  "Participants": [
    {
      "CustomerId": 669616948,
      "FirstName": "ERIC",
      "LastName": "TEST2",
      "BirthYear": 0,
      "BirthMonth": 0,
      "CustomerState": 0,
      "LegalAddressState": null,
      "Username": "ERIC2222"
     }
  ]
}

Currently this throws an exception when it tries to convert to a poco and while we can handle the exception I was looking for a way to detect if the JObject contained an array and gracefully exit.

So the json above is just a representation of what the JObject would look like but it IS a JObject.

The best I have been able to come up with is a measly string check.

JObject.ToString().Contains("[")

Any ideas on how to do an array check. If I could somehow get it to a JToken then I could do this (temp is of type JToken):

temp.Type == JTokenType.Array 

TIA


As requested here is the conversion. payload is a JObject.

var customer = payload.ToObject<Customer>(_serializer);

Upvotes: 1

Views: 5950

Answers (3)

sam
sam

Reputation: 1985

Am not quite sure about your requirements. But this is one way to go:

        [TestMethod]
        public void DeserializeTest()
        {
            var jsonStr1 = "{\"CustomerId\": 669616948,\"FirstName\": \"ERIC\",\"LastName\": \"TEST2\",\"BirthYear\": 0,\"BirthMonth\": 0,\"CustomerState\": 0,\"LegalAddressState\": null,\"Username\": \"ERIC2222\"}";
            JToken token1 = JToken.Parse(jsonStr1);
            var participantsFromToken1 = token1["Participants"];
            Console.WriteLine(participantsFromToken1 != null && participantsFromToken1.Type == JTokenType.Array
                ? "Hey, token1 is an array"
                : "Hey, token1 is not an array");


            var jsonStr2 =
                "{\"Participants\": [{\"CustomerId\": 669616948,\"FirstName\": \"ERIC\",\"LastName\": \"TEST2\",\"BirthYear\": 0,\"BirthMonth\": 0,\"CustomerState\": 0,\"LegalAddressState\": null,\"Username\": \"ERIC2222\"}]}";
            JToken token2 = JToken.Parse(jsonStr2);

            var participantsFromToken2 = token2["Participants"];
            Console.WriteLine(participantsFromToken2 != null && participantsFromToken2.Type == JTokenType.Array
                ? "Hey, token2 is an array"
                : "Hey, token2 is not an array");
        }

Upvotes: 0

David L
David L

Reputation: 33833

You can always write a custom JsonConverter that walks the json tree (using a technique such as that described in this answer https://stackoverflow.com/a/19646950/1165998), checking both the type and the value type for JArray and returning null if it is:

public class ProhibitArraysConverter<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)
    {
        var jsonObject = JToken.Load(reader);
        if (ContainsArray(jsonObject))
            return null;

        T target = (T)Activator.CreateInstance(objectType);
        serializer.Populate(jsonObject.CreateReader(), target);
        return target;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    private static bool ContainsArray(JToken containerToken)
    {
        if (containerToken.Type == JTokenType.Object)
        {
            foreach (JProperty child in containerToken.Children<JProperty>())
            {
                if (child.Type == JTokenType.Array || 
                    child.Value.Type == JTokenType.Array)
                {
                    return true;
                }
                ContainsArray(child.Value);
            }
        }
        else if (containerToken.Type == JTokenType.Array)
        {
            return true;
        }

        return false;
    }
}

This will return the deserialized data in your first example and null for your second.

Upvotes: 1

Ilya Sulimanov
Ilya Sulimanov

Reputation: 7846

What about this way?

dynamic value = jToken["Participants"];
if (value != null && value is JArray)
{
 //gracefully exit.
}

Upvotes: 2

Related Questions