Sunday 7 February 2021

NHS MIM and DMS tools: Code generation, schema and validation

1. Introduction

When working with the NHS projects, I need to deal with a large number of linked schemas, particularly with domains that use the NHS Messaging Implementation Manual or the NHS Domain Messaging Specification. The MIM or DMS is pretty much the same, except MIM covers multiple domains while DMS only covers just one domain. MIM was firstly introduced to cover domains like EPS, Referral (Choice and Book), GP2GP, and PDS. Later one NHS agency chose to publish specification per domain thus DMS was introduced. MIM or DMS contains specification artifacts which are the output from HL7 V3 modelings such as interaction and messaging specification within a domain or multiple domains.

The packs can be downloaded from TRUD  under the Message Specification category. You need to register an account to be able to subscribe and download the pack. The packs contain schemas, and in this article, I will explain the NuGet packages I have created and how to use them. The source code can be downloaded from Github here

There are 5 packages generated from the source code and they can be accessed using NuGet:
  1. Mim.V6301
  2. Mim.V4200
  3. Mim.V3109
  4. Dms.Ambulance.V2100
  5. Messaging.Core
Package 1 to 4 are specification packages that contain schemas and generated c# code for the schema. There are also SchemaNames and TemplateNames which are enumerated schemas contained in the packages. You can use these packages to build a message object in C# code, and serialize them to output an XML message, or you can deserialize them from XML message into C# object. 

However, the code generation does not guarantee the validity of the XML message against the schema. The validation code is provided in other packages, which is Messaging.Core. The package contains Schema Validation, Schematron Validation, NHS renderer, and CDA Validation and can be used in conjunction with the other 4 packages. 

2. How to use MIM and DMS packages

The MIM and DMS packages contain:
  • Schemas files, which are under Schema folder. The structure under this folder follows the schema folder in the specification packs, which can have subfolders; dt, voc, TemplateSchemas, vocSchemas, and Schemas. The schema files are included as embedded resources and can be used in conjunction with Messaging.Core to validate a message against those schemas. 
  • Generated C# code, which is under Generated folder. These are the model class for the schema, and can be used to:
    • Build the object for the schema
    • Serialize the object to an XML string (instance method Serialise)
    • Deserialize to object from an XML string (static method Deserialize)
  • SchemaNames and TemplateSchemas, which enumerates the schema files (without the extension) in the assembly. This can be used when validating a message against schema in the assembly
 

3. How to Use Messaging.Core package

For how to use Messaging.Core in the code you can see the unit tests in github. This article will describe the component in the package and how to use them. The Messaging.Core contains a number of artifacts:

3.1 Resource Resolver  

Namespace: Messaging.Resolvers

The package has resolvers that can resolve resources from either an assembly or from a directory. The resource resolver is inherited from XmlUrlResolver, IResourceResolver. The IResourceResolver has a method with a signature, Stream GetResourceStream(string resourceName), which resolves a resource given its name. When working with resource resolvers,  the resource name is assumed to have two parts: name and extension. There are two types of resource resolvers in Messaging.Core package. 
    3.1.1. EmbeddedResourceResolver

    Signatures:
    • public EmbeddedResourceResolver(Assembly resourceAssembly = null)
    • public static EmbeddedResourceResolver Create(AssemblyType assemblyType)
    • public Stream GetResourceStream(string resourceName)
    • public object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
    As the name implied, this resource resolver locates a resource as an embedded resource in an assembly. The instance constructor takes an assembly as the optional argument, and it will default to the current assembly if the argument is null.  The static constructor takes enumerated AssemblyType, which can have a value of :
    •  AssemblyType.Caller, the assembly which calls Messaging.Core method. This value can be used for unit testing where the resources normally reside within the unit test project.
    • AssemblyType.Library, the Messaging.Core assembly itself. This can be used to resolve resources that reside on the Messaging.Core project. 
    • AssemblyType.EntryHost, the host assembly which executes the code on runtime, like console or web app projects.
    If you use this resource resolver, you can use either package 1-4 assembly to validate a message against a schema in those packages. For the EmbeddedResourceResolver, locates embedded resources in the assembly based on the resources name and ignoring the path. 

    GetResourceStream method is part of IResourceResolver and can be used to resolve a resource name to a content stream. The package provides an extension method GetText to convert the stream to string content. GetEntity method is part of XmlUrlResolver, and normally used by other methods using XmlUrlResolver such as for schema validation, and it is not recommended to use it directly. 

    3.1.2. DirectoryResourceResolver

    Signatures:
    • public DirectoryResourceResolver(string path)
    • public Stream GetResourceStream(string resourceName)
    • public object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
    If you use this resource resolver, you can validate a message against schema in a directory. The constructor takes the directory path as an argument. In order to use DirectoryResourceResolver, you just need to put  DMS schemas or any other schema in a directory and validate a message against that directory. The resolver will locate resources based on the resource name it does not matter where you put the resource in the directory, as long as they are all uniques in that directory. The last two methods are the same as those of AssemblyResourceResolver. 

    3.2. Stylesheet Transformer

    Namespace: Messaging.Transformers

    Signatures:
    • public StylesheetTransformer(ResourceResolver resolver = null)
    • public string Transform(StylesheetDocument stylesheetDocument, string inputXml)
    • public string Transform(string stylesheetXml, string inputXml)
    • public Stream TransformStream(Stream stylesheetStream, Stream inputXmlStream)
    The constructor takes an optional resource resolver and is used to resolve stylesheet references. You don't need one if working with a single stylesheet without any references. It has two Transform methods and a TransformStream method. 

    The first Transform method takes a stylesheet XML and an input XML, and if there are references in the stylesheet XML it uses the resource resolver passed in the constructor to resolve the resources. The second Transform method takes an enumerated stylesheet in the Messaging.Core and an input XML. The enumerated stylesheet is specific to MIM and DMS, which are:
    • CdaToTemplated
    • This is used within a CDA validator to transform a CDA message ('on wire' format)  to a templated format before validating against the template schema.
    • TemplatedToCda
    • This is not currently in use at the moment, but it does the reverse to the CdaToTemplated
    • CdaRenderer
    • This stylesheet is used to render on wire CDA message to a viewable HTML. 
    • Nhs111PrimaryCdaRenderer
    • This stylesheet is used to render on wire CDA message to viewable HTML on the requirement for NHS111 domain (Ambulance domain)
    • PostConversion
    • This is used in conjunction with CDAtoTemplated and TemplatedToCDA
    The last method, TransformStream, takes stream instead of raw XML for both stylesheet and input XML. There is an extension method in the Messaging.Core that converts from raw XML into a stream called GetStream.

    3.3. SchemaValidator

    Namespace: Messaging.Validators

    Signatures:
    • public SchemaValidator(ResourceResolver externalResolver)
    • public static SchemaValidator Create(Assembly externalAssembly)
    • public static SchemaValidator Create(string folder)
    • public Result Validate(string schemaName, string inputXml)
    • public Result Validate(Stream schemaStream, Stream inputXmlStream)
    The schema validator has a constructor that takes a resource resolver and also two static constructors that take either a folder path (uses DirectoryResourceResolver) or an assembly (use EmbeddedResourceResolver). 

    The first Validate method takes a schema name and input XML. The schema name is resolved to a resource by the resource resolver passed in the constructor. The schema name is the name of the schema with or without an xsd extension, for example, it can be COCD_TP146045UK05Author2 or COCD_TP146045UK05Author2.xsd. If you a single schema resource, you can convert it to a stream, e.g using GetStream method, and use the second Validate method which takes a schema stream instead. 

    The return of Validate method is Result, which contains boolean Status, a string Description, and optionally an Exception

    3.4. SchematronValidator

    Namespace: Messaging.Validators

    Signatures:
    • public SchematronValidator(string schematronContent, bool convertToIsoNamespace = false)
    • public SchematronValidator(Stream schematronStream)
    • public static SchematronValidator Create(SchematronDocument schematronDocument)
    • public Result Validate(string inputXml)
    • public Result Validate(Stream inputStream)
    SchematronValidator is an iso Schematron compliant validator. It has two instance constructor and one static constructor. The first instance constructor takes a Schematron content XML and a flag convertToIsoNamespace. The flag is used to indicate whether or not to convert the namespace in the Schematron content if the old XML namespace is found. The old Schematron content has an XML namespace 'http://www.ascc.net/xml/schematron', while the new Schematron content has an XML namespace of 'http://purl.oclc.org/dsdl/schematron'. Without the conversion, the validator will fail to validate a message if the old Schematron content is passed in the constructor. The static constructor takes an enumerated Schematron content which is located in the Messaging.Core assembly. The enumerated Schematron is MIM and DMS specific as follows:
    • GenericCda
    • Schematron to validate generic CDA 'on wire' format messages. This Schematron is used in the CdaValidator.
    • TemplatedCda
    • Schematron to validate templated CDA messages, which are messages transformed from generic CDA messages using the enumerated TemplatedToCda stylesheet. This Schematron is also used in the CdaValidator
    • GenericCdaInteraction
    • Schematron to validate HL7 V3 interaction messages.
    • PsisQuery
    • Schematron to validate PSIS Query message 
    In order to validate an XML message against the Schematron content use Validate method. It has two variants. The first variant takes a raw XML while the other takes a stream. 

    3.5. CdaValidator

    Namespace: Messaging.Validators

    Signatures:
    • public CdaValidator(ResourceResolver externalResolver)
    • public static CdaValidator Create(Assembly externalAssembly)
    • public static CdaValidator Create(string folder)
    • public Result Validate(string inputXml, string templateSchemaName = null, string cdaSchemaName = null)
    CDA validator is composed of series of validation, generic CDA and templated CDA messages validation, and a stylesheet transformation. The templated message is a HL7 V3 modeling of the message, and there is a number constraint on a model level. The steps involved in the CDA validator are:
    • Validate generic CDA message against generic CDA schema
    • Validate generic CDA message against Schematron of generic CDA
    • Transform generic CDA message to templated CDA message
    • Validate templated CDA against templated CDA schema
    • Validate templated CDA against schematron of templated CDA 
    The constructor is similar to that of SchemaValidator, which has an instance constructor which takes a resource resolver, and two static constructors that takes either a directory path or an assembly. The Validate method takes inputXml string and optional templateSchemaName, which value is with or without xsd extension. Normally in a CDA message, the generic schema and the templated schema can be retrieved from input XML. If optional templateSchemaName is specified, the validation uses this template schema and also makes sure this is the same template schema specified in the inputXML, if it is present. Similarly, if optional cdaSchemaName is present.

    As a side note, the Messaging.Core can be used outside the DMS and MIM packages. It can be used with any complex schema or a simple one. In order to use a directory resource resolver, you can just put all your schema in a directory, and create a DirectoryResourceResolver object passing the directory to the constructor, and then you are on your way to use validators and transformer in Messaging.Core
    Alternatively, you can put all your schema in an assembly as embedded resources and create an EmbeddedResourceResolver object by passing the assembly in the constructor. Alternatively, you can implement your own resolver, implementing ResourceResolver.


    No comments:

    Post a Comment