When using the Newtonsoft.Json library, there are several problems with navigating through a JToken, especially with clunkiness and error handling.
{
"someObject": {
"someInnerObject": {
"innerInnerValue": 5
}
}
}public static long GetInnerInnerValue(string jsonStr)
{
JObject jobject = JsonConvert.DeserializeObject<JObject>(jsonStr)!;
JValue innerInnerJvalue = (JValue)jobject["someObject"]!["someInnerObject"]!["innerInnerValue"]!;
long innerInnerValue = (long)innerInnerJvalue.Value!;
return innerInnerValue;
}
public static long? GetInnerInnerValueOrNull(string jsonStr)
{
JObject? jobject = JsonConvert.DeserializeObject<JObject>(jsonStr);
JValue? innerInnerJvalue = (JValue?)jobject?["someObject"]?["someInnerObject"]?["innerInnerValue"];
long? innerInnerValue = innerInnerJvalue?.Value as long?;
return innerInnerValue;
}
public static long GetInnerInnerValueOrThrow(string jsonStr)
{
JObject jobject = JsonConvert.DeserializeObject<JObject>(jsonStr) ?? throw new JsonContentException("Could not parse JSON string");
JToken someObjectJtoken = jobject["someObject"] ?? throw new JsonContentException("someObject not found in JSON");
JObject someObject = someObjectJtoken as JObject ?? throw new JsonContentException("someObject is not a valid JSON object");
JToken someInnerObjectJtoken = someObject["someInnerObject"] ?? throw new JsonContentException("someInnerObject not found in JSON");
JObject someInnerObject = someInnerObjectJtoken as JObject ?? throw new JsonContentException("someInnerObject is not a valid JSON object");
JToken innerInnerValueJtoken = someInnerObject["innerInnerValue"] ?? throw new JsonContentException("innerInnerValue not found in JSON");
JValue innerInnerValueJvalue = innerInnerValueJtoken as JValue ?? throw new JsonContentException("innerInnerValue does not have a valid JSON value");
long innerInnerValue = innerInnerValueJvalue.Value as long? ?? throw new JsonContentException("innerInnerValue is not a valid integer");
return innerInnerValue;
}
public class JsonContentException(string? message) : Exception(message);// automatically throws the appropriate JsonContentException and explains where the problem is
// JsonUtils.Deserialize tries to deserialize or throws a JsonContentException if the string is empty
// (Newtonsoft.Json already throws JsonException if the JSON itself is invalid)
// the D method stands for Dereference -
// it dereferences a JSON object or throws a JsonContentException explaining why it can't do that
// the As<T> method converts a JToken to type T (supports values, strings, JToken subtypes, etc.)
// or throws a JsonContentException explaining why it can't do that
public static long GetInnerInnerValue(string jsonStr)
{
JObject jobject = JsonUtils.Deserialize(jsonStr);
long innerInnerValue = jobject.D("someObject").D("someInnerObject").D("innerInnerValue").As<long>();
return innerInnerValue;
}
// when a method name has N appended to it, it does the same thing but returns null instead of throwing an error
// so DN dereferences or returns null, AsN<T> converts to type T or returns null
public static long? GetInnerInnerValueOrNull(string jsonStr)
{
JObject? jobject = JsonConvert.DeserializeObject<JObject>(jsonStr);
long? innerInnerValue = jobject?.DN("someObject")?.DN("someInnerObject")?.DN("innerInnerValue")?.AsN<long>()?.Value;
return innerInnerValue;
}
// GetInnerInnerValue already handles exceptions, so no need for special implementation
public static long GetInnerInnerValueOrThrow(string jsonStr) => GetInnerInnerValue(jsonStr);// deserialize or throw JsonException if JSON is invalid, or JsonContentException if string is empty
JObject Deserialize(string jsonString);
// convert a JToken to type T or throw JsonContentException
T As<T>(this JToken jtoken) where T : notnull;
// convert a JToken to type T or return null
Wrap<T>? AsN<T>(this JToken jtoken) where T : notnull;
// return true if jtoken is of type T, or if it is a JValue and its value is of type T, false otherwise
bool Is<T>(this JToken jtoken) where T : notnull;
// return true if jtoken is a JValue and its value is null, false otherwise
bool IsNull(this JToken jtoken);
// dereference the jtoken and return a new JToken, or throw JsonContentException
JToken D(this JToken jtoken, string propertyName);
// dereference the jtoken and return a new JToken, or return null
JToken? DN(this JToken jtoken, string propertyName);
// dereference the jtoken and convert it to type T, or throw JsonContentException
T D<T>(this JToken jtoken, string propertyName) where T : notnull;
// dereference the jtoken and convert it to type T, or return null
Wrap<T>? DN<T>(this JToken jtoken, string propertyName) where T : notnull;
// return true if jtoken is a JObject and contains a property with name propertyName, false otherwise
bool ContainsProperty(this JToken jtoken, string propertyName);
// return true if jtoken is a JObject and contains a property with name propertyName and is of type T,
// where T is either a JToken (or subtype) or a JSON value such as string/long; false otherwise
bool ContainsProperty<T>(this JToken jtoken, string propertyName) where T : notnull;
// return true if jtoken is a JObject and contains a property with name propertyName and gives the property as a subtoken;
// otherwise returns false and gives a null subtoken
bool ContainsProperty(this JToken jtoken, string propertyName, out JToken? subtoken);
// return true if jtoken is a JObject and contains a property with name propertyName and is of type T,
// and gives the property as a subtoken; otherwise returns false and gives a null subtoken
bool ContainsProperty<T>(this JToken jtoken, string propertyName, out Wrap<T>? subtoken) where T : notnull;
// return the JToken with the specified index of a JArray, or throw JsonContentException if jtoken is not an array or if out of range
JToken At(this JToken jtoken, int index);
// return the JToken with the specified index of a JArray, or return null
JToken? AtN(this JToken jtoken, int index);
// return the value of type T with the specified index of a JArray, or throw JsonContentException if jtoken is not an array,
// or if out of range
T At<T>(this JToken jtoken, int index) where T : notnull;
// return the value of type T with the specified index of a JArray, or return null
Wrap<T>? AtN<T>(this JToken jtoken, int index) where T : notnull;
// set the value of jtoken to null, or throw JsonContentException if jtoken is not a JValue
void SetNull(this JToken jtoken);
// set the value of jtoken to null and return true, or return false if jtoken is not a JValue
bool TrySetNull(this JToken jtoken);
// set the value of jtoken to value, or throw JsonContentException if jtoken is not a JValue or if T is invalid
// T can only be a direct JSON value like string/long
void Set<T>(this JToken jtoken, T value);
// set the value of jtoken to value and return true, or return false if jtoken is not a JValue or if T is invalid
// T can only be a direct JSON vlaue like string/long
bool TrySet<T>(this JToken jtoken, T value);
// set the given property of jtoken to null or throw JsonContentException
// NOTE: this is different from SetNull(this JToken jtoken) in that if this property already existed,
// it replaces the entire jtoken with a JValue (with a value of null)
// in particular, this method can be called even if the property was not previously a JValue
void SetNull(this JToken jtoken, string propertyName);
// set the given property of jtoken to null and return true, or return false if not possible
// NOTE: if the property already existed, this replaces the entire jtoken with a JValue (with a value of null)
bool TrySetNull(this JToken jtoken, string propertyName);
// set the given property of jtoken to value or throw JsonContentException
// NOTE: if the property already existed, this replaces the entire jtoken with a new one
// T can either be JToken (or a subtype), or a direct JSON value like string/long
void Set<T>(this JToken jtoken, string propertyName, T value);
// set the given property of jtoken to value and return true, or return false if not possible
// NOTE: if the property already existed, this replaces the entire jtoken with a new one
// T can either be JToken (or a subtype), or a direct JSON value like string/long
bool TrySet<T>(this JToken jtoken, string propertyName, T value);
// set the given element of a JArray to null or throw JsonContentException
// NOTE: this replaces the entire jtoken at that element with a JValue (with a value of null)
void SetNull(this JToken jtoken, int index);
// set the given element of a JArray to null and return true, or return false if not possible
// NOTE: this replaces the entire jtoken at that element with a JValue (with a value of null)
bool TrySetNull(this JToken jtoken, int index);
// set the given element of a JArray to value or throw JsonContentException
// NOTE: this replaces the entire jtoken with a new one
// T can either be JToken (or a subtype), or a direct JSON value like string/long
void Set<T>(this JToken jtoken, int index, T value);
// set the given element of a JArray to value and return true, or return false if not possible
// NOTE: this replaces the entire jtoken with a new one
// T can either be JToken (or a subtype), or a direct JSON value like string/long
bool TrySet<T>(this JToken jtoken, int index, T value);
// deep clone the jtoken and convert the result to type T or throw JsonContentException
T DeepClone<T>(this JToken jtoken) where T : notnull;
// deep clone the jtoken and convert the result to type T or return null if conversion is invalid
Wrap<T>? DeepCloneN<T>(this JToken jtoken) where T : notnull;