Defensive data consumption

Data is subject to change

It is best to assume that all data outside of the substantial number of fields defined in the OpenActive modelling specification and imin namespace is subject to change at any time. For more information on the rationale for this, see Namespaces and Deprecation.

This guide details best practices to help you code defensively to allow for such changes.

If you are considering using one of the unstable fields to drive critical production functionality, please get in touch with us at hello@imin.co and we will work with you to propose your use case to the OpenActive W3C Community Group, and move the field into either the OpenActive or imin namespace.

Gracefully handling missing data

Regardless of whether or not a property is a required field in the OpenActive modelling specification, or whether it is assumed to be present in all cases (e.g. "imin:fullAddress"), it should be assumed that there will be an item of data returned where this property is missing or very short (e.g. only "N4 3FL").

Adaptive User Interfaces

Panels (e.g. <div>) used to display properties within user interfaces should collapse if the value of the property is not present.

For example, the "time" icon in the image below should be hidden if the duration field is not present.

For another example, the design should anticipate a long and short imin:fullAddress.

imin:fullAddress could be very long
imin:fullAddress could also be short

Where a dedicated page is available for details on a single EventSeries or FacilityUse, ensure that the view does not look sparse when minimal data is available, and even consider designing two versions of the details page to cover both scenarios.

Adaptive Calculations

For calculations, ensure that missing or unexpected data is factored in.

For example for an aggregateRating (not currently part of the OpenActive modelling specification, and only defined in schema.org, see Namespaces for more info) if your application has a 5-star rating system and an Event is presented where the reviewRating is 10 but the bestRating is not defined (and defaulted to 5 according to schema.org), your calculation should treat the aggregateRating as missing completely if the calculation has insufficient data, instead of displaying 10 stars or a division-by-zero error.

"aggregateRating": {
"type": "AggregateRating",
"reviewCount": 45,
"ratingValue": 10,
"worstRating": 0
}

Missing images

For image URLs, we do not currently monitor the image URLs provided in open data in real-time, and while we are always cleansing the raw data and working with providers to improve the quality of their data, there will be rare cases where an image URL is invalid.

We recommend that you use CSS to display a single default image or subtle background gradient if the image supplied doesn't render, to ensure that the user experience is not adversely affected in the rare case that an image is unavailable.

Robust data processing

In JavaScript accessing deeply nested properties and making assumptions about the existence of properties can lead to unexpected errors for data structures with a high degree of variance.

JSON Query Libraries

A number of JSON query libraries also exist that facilitate safe querying of data.

We conducted a benchmark of the most commonly used libraries for JSON Querying, and the results clearly show JSPath to be the most performant by far. It significantly out-performed a plain JavaScript functional approach ("control"), and as such is our recommended default approach.

Speed comparison of JavaScript query libraries

Example queries

When expecting an array result, JSPath will alway return an array, with [] returned in the case of no matches.

jspath-demo.js
return JSPath.apply('.subEvent.offers."beta:availableChannel"', result).join('<br />');

When expecting a single object or primitive, specifying [-1] at the end of the query will result in the query returning either the expected value, or undefined in the case of no matches.

// Checks for existance of valid price and priceCurrency
const offer = JSPath.apply('."imin:aggregateOffer".publicAdult{.price > 0 && .priceCurrency > "."}[-1]', result);
// Always use browser built-in toLocaleString to render price
return offer ? offer.price.toLocaleString('en', {
style: 'currency',
currency: offer.priceCurrency
}) : '';

Additionally, if particular properties within a type are necessary for your processing, ensure they exist by including an existence check in the query such as .startTime and .endTime in the example below:

var scheduleDates = JSPath.apply('.subEvent.eventSchedule{.type === "PartialSchedule" && .byDay === $day && .startTime && .endTime}', result, { day : 'schema:' + matchingDay })

The Online JSON Query Test supports JSPath directly, and is our recommended tool for developing and testing JSON Queries.

Other available libraries

Alternative approaches

There are a number of techniques to accessing property of a JSON object in a robust way, discussed in the following article. These are useful for simple implementations, where performance is less of a concern.

An extension of the most basic of these implementations allows for accessing of arrays and properties:

const get = p => o =>
p.reduce((xs, x) => {
if (Array.isArray(xs)) {
return xs.map(get([x])).filter(y => y !== null).flat();
} else {
return (xs && xs[x]) ? xs[x] : null;
}
}, o);
// Example use
return get(['subEvent', 'offers', 'acceptedPaymentMethod'])(result).join('<br />');