84

I am using .net web api to get json and return it to the front end for angular. The json can be either an object or an array. My code currently only works for the array not the object. I need to find a way to tryparse or determine if the contents are an object or array.

Here is my code

    public HttpResponseMessage Get(string id)
    {
        string singleFilePath = String.Format("{0}/../Data/phones/{1}.json", AssemblyDirectory, id);
        List<Phone> phones = new List<Phone>();
        Phone phone = new Phone();
        JsonSerializer serailizer = new JsonSerializer();

        using (StreamReader json = File.OpenText(singleFilePath))
        {
            using (JsonTextReader reader = new JsonTextReader(json))
            {
                //if array do this
                phones = serailizer.Deserialize<List<Phone>>(reader);
                //if object do this
                phone = serailizer.Deserialize<Phone>(reader);
            }
        }

        HttpResponseMessage response = Request.CreateResponse<List<Phone>>(HttpStatusCode.OK, phones);

        return response;
    }

The above may not be best way of doing this. Its just where I am now.

4 Answers 4

196

Using Json.NET, you could do this:

string content = File.ReadAllText(path);
var token = JToken.Parse(content);

if (token is JArray)
{
    IEnumerable<Phone> phones = token.ToObject<List<Phone>>();
}
else if (token is JObject)
{
    Phone phone = token.ToObject<Phone>();
}
Sign up to request clarification or add additional context in comments.

11 Comments

you could just check the first charcter, isArray = content[0] == '['
@johnny5 Manually parsing when a good lib is available is generally not a good idea. Case in point, you forgot to check for whitespace :) " []" is a valid json array.
that's why I didn't post it as an answer, because it's a hack but it's note worthy.
how much overhead is there in creating the jToken, isn't that essentially deserializing it?
@johnny5 "it's note worthy" - agreed. Yes, creating the JToken is basically deserializing a string to Map<string, object> (no reflection needed). But the OP was already deserializing it anyway.
|
9

I found that the accepted solution using Json.NET is a bit slow for large JSON files.
It appears that the JToken API is performing too many memory allocations.
Here is a helper method that uses the JsonReader API with the same result:

public static List<T> DeserializeSingleOrList<T>(JsonReader jsonReader)
{
    if (jsonReader.Read())
    {
        switch (jsonReader.TokenType)
        {
            case JsonToken.StartArray:
                return new JsonSerializer().Deserialize<List<T>>(jsonReader);

            case JsonToken.StartObject:
                var instance = new JsonSerializer().Deserialize<T>(jsonReader);
                return new List<T> { instance };
        }
    }

    throw new InvalidOperationException("Unexpected JSON input");
}

The usage:

public HttpResponseMessage Get(string id)
{
    var filePath = $"{AssemblyDirectory}/../Data/phones/{id}.json";

    using (var json = File.OpenText(filePath))
    using (var reader = new JsonTextReader(json))
    {
        var phones = DeserializeSingleOrList<Phone>(reader);

        return Request.CreateResponse<List<Phone>>(HttpStatusCode.OK, phones);
    }
}

Comments

7

Asthetically I like the answer @dcastro gave better. But, if you are generating a JToken object, you can also just use the Type enum property of the token. It's possibly less expensive then doing an object type comparison, as the Type property has already been determined.

https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JTokenType.htm

//...JToken token
if (token.Type == JTokenType.Array)
{
    IEnumerable<Phone> phones = token.ToObject<List<Phone>>();
}
else if (token.Type == JTokenType.Object)
{
    Phone phone = token.ToObject<Phone>();
}
else
{
    Console.WriteLine($"Neither, it's actually a {token.Type}");
}

2 Comments

Looks like this is not working always. Sometimes I get an object type while my json string has an array. Any experience?
@StevenCool I have not come across that, so can't say what would be causing it.
7

If you are using .NET Core 3.1, you can use the following check on the JsonElement object.

using System.Text.Json;

public void checkJsonElementType(JsonElement element) {
    switch (element.ValueKind)
    {
        case JsonValueKind.Array:
            // it's an array
            // your code in case of array
            break;
        case JsonValueKind.Object:
            // it's an object
            // your code in case of object
            break;
        case JsonValueKind.String:
            // it's an string
            // your code in case of string
            break;
       .
       .
       .
    }
}

Allowed values of JsonValueKind are Array, False, Null, Number, Object, String, True, Undefined

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.