Get value from JToken that may not exist (best practices)

C#json.net

C# Problem Overview


What's the best practice for retrieving JSON values that may not even exist in C# using [Json.NET][1]?

Right now I'm dealing with a JSON provider that returns JSON that sometimes contains certain key/value pairs, and sometimes does not. I've been using (perhaps incorrectly) this method to get my values (example for getting a double):

if(null != jToken["width"])
    width = double.Parse(jToken["width"].ToString());
else
    width = 100;

Now that works fine, but when there are a lot of them it's cumbersome. I ended up writing an extension method, and only after writing it did I wonder whether maybe I was being stupid... anyways, here is the extension method (I only include cases for double and string, but in reality I have quite a few more):

public static T GetValue<T>(this JToken jToken, string key,
                            T defaultValue = default(T))
{
	T returnValue = defaultValue;

	if (jToken[key] != null)
	{
		object data = null;
		string sData = jToken[key].ToString();

		Type type = typeof(T);

		if (type is double)
			data = double.Parse(sData);
		else if (type is string)
			data = sData;

		if (null == data && type.IsValueType)
			throw new ArgumentException("Cannot parse type \"" + 
				type.FullName + "\" from value \"" + sData + "\"");

		returnValue = (T)Convert.ChangeType(data, 
			type, CultureInfo.InvariantCulture);
	}

	return returnValue;
}

And here's an example of using the extension method:

width = jToken.GetValue<double>("width", 100);

BTW, Please forgive what may be a really dumb question, since it seems like something there should be a built in function for... I did try Google, and [Json.NET][1] documentation, however I'm either inept at finding the solution to my question or it's not clear in the documentation.

[1]: http://json.codeplex.com "JSON.net"

C# Solutions


Solution 1 - C#

This is pretty much what the generic method Value() is for. You get exactly the behavior you want if you combine it with nullable value types and the ?? operator:

width = jToken.Value<double?>("width") ?? 100;

Solution 2 - C#

I would write GetValue as below

public static T GetValue<T>(this JToken jToken, string key, T defaultValue = default(T))
{
    dynamic ret = jToken[key];
    if (ret == null) return defaultValue;
    if (ret is JObject) return JsonConvert.DeserializeObject<T>(ret.ToString());
    return (T)ret;
}

This way you can get the value of not only the basic types but also complex objects. Here is a sample

public class ClassA
{
    public int I;
    public double D;
    public ClassB ClassB;
}
public class ClassB
{
    public int I;
    public string S;
}

var jt = JToken.Parse("{ I:1, D:3.5, ClassB:{I:2, S:'test'} }");

int i1 = jt.GetValue<int>("I");
double d1 = jt.GetValue<double>("D");
ClassB b = jt.GetValue<ClassB>("ClassB");

Solution 3 - C#

Here is how you can check if the token exists:

if (jobject["Result"].SelectToken("Items") != null) { ... }

It checks if "Items" exists in "Result".

This is a NOT working example that causes exception:

if (jobject["Result"]["Items"] != null) { ... }

Solution 4 - C#

You can simply typecast, and it will do the conversion for you, e.g.

var with = (double?) jToken[key] ?? 100;

It will automatically return null if said key is not present in the object, so there's no need to test for it.

Solution 5 - C#

This takes care of nulls

var body = JObject.Parse("anyjsonString");

body?.SelectToken("path-string-prop")?.ToString();

body?.SelectToken("path-double-prop")?.ToObject<double>();

Solution 6 - C#

TYPE variable = jsonbody["key"]?.Value<TYPE>() ?? DEFAULT_VALUE;

e.g.

bool attachMap = jsonbody["map"]?.Value<bool>() ?? false;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPaul HazenView Question on Stackoverflow
Solution 1 - C#svickView Answer on Stackoverflow
Solution 2 - C#L.BView Answer on Stackoverflow
Solution 3 - C#Artur AlexeevView Answer on Stackoverflow
Solution 4 - C#Dave Van den EyndeView Answer on Stackoverflow
Solution 5 - C#MaxView Answer on Stackoverflow
Solution 6 - C#DownhillskiView Answer on Stackoverflow