Deserialize json in a "TryParse" way

.net, c#, json

Solution

With `Json.NET` you can validate your json against a schema:

 string schemaJson = @"{
 'status': {'type': 'string'},
 'error': {'type': 'string'},
 'code': {'type': 'string'}
}";

JsonSchema schema = JsonSchema.Parse(schemaJson);

JObject jobj = JObject.Parse(yourJsonHere);
if (jobj.IsValid(schema))
{
    // Do stuff
}

And then use that inside a TryParse method.

public static T TryParseJson<T>(this string json, string schema) where T : new()
{
    JsonSchema parsedSchema = JsonSchema.Parse(schema);
    JObject jObject = JObject.Parse(json);

    return jObject.IsValid(parsedSchema) ? 
        JsonConvert.DeserializeObject<T>(json) : default(T);
}

Then do:

var myType = myJsonString.TryParseJson<AwsomeType>(schema);

Update:

Please note that schema validation is no longer part of the main Newtonsoft.Json package, you'll need to add the Newtonsoft.Json.Schema package.

Update 2:

As noted in the comments, "JSONSchema" have a pricing model, meaning it isn't free. You can find all the information here

Problem

When I send a request to a service (that I do not own), it may respond either with the JSON data requested, or with an error that looks like this: ``` { "error": { "status": "error message", "code": "999" } } ``` In both cases the HTTP response code is 200 OK, so I cannot use that to determine whether there is an error or not - I have to deserialize the response to check. So I have something that looks like this: ``` bool TryParseResponseToError(string jsonResponse, out Error error) { // Check expected error keywords presence // before try clause to avoid catch performance drawbacks if (jsonResponse.Contains("error") && jsonResponse.Contains("status") && jsonResponse.Contains("code")) { try { error = new JsonSerializer<Error>().DeserializeFromString(jsonResponse); return true; } catch { // The JSON response seemed to be an error, but failed to deserialize. // Or, it may be a successful JSON response: do nothing. } } error = null; return false; } ``` Here, I have an empty catch clause that may be in the standard execution path, which is a bad smell... Well, more than a bad smell: it stinks. Do you know a better way to "TryParse" the response in order to avoid a catch in the standard execution path ? [EDIT] Thanks to Yuval Itzchakov's answer I improved my method like that : ``` bool TryParseResponse(string jsonResponse, out Error error) { // Check expected error keywords presence : if (!jsonResponse.Contains("error") || !jsonResponse.Contains("status") || !jsonResponse.Contains("code")) { error = null; return false; } // Check json schema : const string errorJsonSchema = @"{ 'type': 'object', 'properties': { 'error': {'type':'object'}, 'status': {'type': 'string'}, 'code': {'type': 'string'} }, 'additionalProperties': false }"; JsonSchema schema = JsonSchema.Parse(errorJsonSchema); JObject jsonObject = JObject.Parse(jsonResponse); if (!jsonObject.IsValid(schema)) { error = null; return false; } // Try to deserialize : try { error = new JsonSerializer<Error>.DeserializeFromString(jsonResponse); return true; } catch { // The JSON response seemed to be an error, but failed to deserialize. // This case should not occur... error = null; return false; } } ``` I kept the catch clause... just in case.

Original source

Related problems