Practical .NET

Validating JSON with JSON Schema

Once you've created a JSON Schema that describes a JSON document, you can use it both in Visual Studio -- to provide guidance when creating JSON documents -- and in your code to validate the messages you're receiving.

In an earlier column, I showed how to create a JSON schema that describes the format of a valid JSON document. I also showed how to use that schema to provide support for developers creating JSON documents in Visual Studio (or any other editor that supports JSON Schemas).

But you can also use your JSON schemas at run time to validate JSON files or messages, ensuring your code doesn't try to process something that isn't correctly formatted.

Validating at Run Time
A number of packages support JSON validation with schemas, but NewtonSoft.Json.Schema is easily available through NuGet (if you're working in ASP.NET Core there's currently a prerelease version available). The package contains two objects that you can use for validation: JsonSchema (which is deprecated) and the newer, cooler JSchema (which is what you should use). NewtonSoft's package is free as long as you're doing fewer than 1,000 validations per hour -- if you think you'll be doing more than that, you should check out their pricing.

You can use NewtonSoft's JSchema and JObject classes to parse JSON strings and then check to see if the JSON string is valid against the schema. The code assumes that the schema is in a string variable called jsonSchema and the actual JSON is in a string variable called jsonInput:

Dim js As JSchema
js = JSchema.Parse(jsonSchema)
Dim jo As JObject
jo = JObject.Parse(jsonInput)

I suspect that this code isn't reasonable, at least for processing the schema -- you're probably keeping your schema in a file. The simplest way to load your JSchema and JObject classes from files is to use the File object's ReadAllText method, like this:

Dim js As JSchema
js = JSchema.Parse(File.ReadAllText("RequestCustomerSchema.json"))
Dim jo As JObject
jo = JObject.Parse(File.ReadAllText("CustomerRequest.json"))
In a Web service, the most likely combination is that you'll read your schema from a file but will be accepting your JSON document as a parameter to an ASP.NET Web API method.

Regardless of how you load the two objects, to check that the JSON document is valid against the schema, you call the JObject's IsValid method passing the JSchema object and a collection declared as an IList of String. That collection will be populated with any error messages that are found. Typical code using the IsValid method would look like this:

Dim errors As IList(Of String) = Nothing
If Not jo.IsValid(js, errors) Then 
  For Each err As String In errors
    '...assemble error message collection to return to the client...
  Next
  Return ErrorMessages
End If

In C#, the second parameter that holds the error messages must be marked as out.

Validating and Creating .NET Objects
Using NewtonSoft's tools like this still, however, leaves you with a JSON document to parse into .NET Framework variables. With a little more code, you can convert your JSON object into a .NET Framework object with validation done automatically as part of the conversion. The first step is that same as above.

You'll need to load a JSchema object using the code you've already seen.

Your next step is to get your JSON document into a TextReader. If you're reading your JSON document from a file, you can use code like this:

Dim tr As TextReader
tr = File.OpenText("CustomerRequest.json")

If you're dealing with a string of JSON stored in a parameter called -- for example, jsonInput -- then you'd use this code:

Dim tr As TextReader
tr = New StringReader(jsonInput)

Your next step is to pass that TextReader to NewtonSoft's JsonTextReader, like this:

Dim jr As JsonTextReader
jr = New JsonTextReader(tr)

That JsonTextReader must then be passed to NewtonSoft's JSchemaValidatingReader. You'll need to set that reader's Schema property to your JSchema object, like this:

Dim vr As JSchemaValidatingReader
vr = New JSchemaValidatingReader(jr)
vr.Schema = js

You're now ready to generate your .NET Framework objects from your JSON document by creating a JsonSerializer and using its Deserialize method, passing your ValidatingReader. The Deserializer will return a .NET Framework object based on the elements it finds in the JSON document:

Dim jsr As New JsonSerializer()
Dim cr As CustomerRequest
cr = jsr.Deserialize(Of CustomerRequest)(vr)

Your .NET Framework object will need to be a class whose property names match the elements of the JSON object and have compatible data types. This JSON document and class would be compatible, for example:

{
   "customerId": "A123"
}
Public Class CustomerRequest
   Public Property CustomerId As String
End Class

Used like this, the Deserialize method will throw an exception when an error is found. Normally, I'm not comfortable with the overhead of throwing an exception, but this is probably what you want -- you don't want to try and process an invalid JSON message.

Typical code to catch and report validation errors might look like this:

Try
  cr = jsr.Deserialize(Of CustomerRequest)(vr)
Catch ex As JSchemaException
  '...log error information...
   Throw New Exception("Invalid format for message", ex)
End Try

NewtonSoft also includes a validating writer that you may want to use with the JsonSerializer's Serialize method when converting .NET Frameowrk classes to JSON documents. I'd recommend using this during testing to ensure you're creating valid output, but you should bypass the validating writer in production to reduce overhead.

Armed with these tools (and a JSON schema) you can ensure that you don't try to process a message that doesn't live up to your standards.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

Subscribe on YouTube