Concept

jsonRegExp

Store RegExp as JSON string without an unreadable mess of escaping / quoting metacharacters

hh lohmann <hh.lohmann@gmail.com>

Changelog

Last 3 changes - see CHANGELOG file for full list and details

Synopsis

JSON has no own type for Regular Expressions, so they are shoehorned into strings that quickly become unreadable due to necessary escaping / quoting of metacharacters, enforced by having also the RegExp quoting character \ to be quoted with the JSON / JavaScript quoting character that happens to be also \. Alternatively sometimes objects are used to represent the structure of a Regular Expression, but this is also not what you would expect as an immediately redable RegExp.

jsonRegExp is a suggestion to store Regular Expressions as strings by escaping / quoting characters that are not meant to be literal characters instead the usual way of escaping / quoting characters that are meant to be literal characters.

RegExp need, knowledge and tooling

The exact content of a JSON string may be of not much interest if there is some handy tooling between it and its input / output interfaces, but things look different if there is no such tooling or only with clumsy overhead - or if you are the one to build / maintain this tooling.

If you have to deal more directly with formulating and storing RegExps, you may find this less enervating if you are well experienced with formulating and storing RegExps, but not if you are simply forced to use a specific RegExp feature in a primarily non-RegExp mindset.

A typical situation where jsonRegExp may be preferred also with well seasoned RegExp craftsmanship are configuration settings that should be quick and easy to define, inspect and change.

The usual way: RegExp as default, Literal reading as exception

Using strings to store RegExp usually means to force such string to be read always and only as RegExp so that e.g.

new RegExp('a+b')

would match ab, but not a+b

new RegExp('a+b').test('ab') // true
new RegExp('a+b').test('a+b') // false

You might wish for something like enclosing a+b into / to distiguish a RegExp reading with /a+b/ from a literal reading with 'a+b', but since you are forced to use strings in JSON you would only have '/a+b/' vs. 'a+b' and it should be obvious that there cannot be an easy way to distinguish a literal vs. a metacharacter usage of / in '/a+b/', you would need some extra marker for this.

But even if you had such a marker, e.g. the actual existing \/ for a literal / and / without preceding \ as delimiter for Regular Expressions, you would only have the choice between a clear “non-RegExp” a+b and a “fully-RegExp” /a+b/ where you would have to mark “literal exceptions” like a . that is not meant to be RegExp’s “any character”, e.g. a+\.b what enforces in JSON / JavaScript another \ to quote the first one, i.e. a+\\.b.

Now imagine you need a RegExp to match a sequence like

...

that you would either have to quote like

^\\.\\.\\.$

or express as

^\\.{3}$

each with ^ and $ to express “exactly 3” and not (inadvertently) “at least 3”.

You may also have to match strings that contain a reference to JSON in different lower / upper case spellings, i.e. with “JSON” as well as with “json” or even “Json”. With a RegExp you can express this easily as

/JSON/i

but there is no way to store this in a string since the delimiter / to mark i as a flag would be interpreted by reading a string '/JSON/i' as a literal /, i.e. instead of your intended RegExp you will end with

/\/JSON\/i/

The jsonRegExp way: Literal reading as default, RegExp as exception

Instead of assuming that the content of a string is to be interpreted fully by RegExp mechanics except at positions that are explicitly marked to be interpreted literally you could go the other way round by assuming that the content is to be interpreted fully literally except at positions that are explicitly marked to be interpreted by RegExp mechanics.

The first requirement is a marker for “interpret the next character not literally but by RegExp mechanics”, i.e. a character that is neither already a RegExp or otherwise metacharacter, rather rarely used as a literal character but nevertheless available on a common keyboard, has good readability and ideally is easy to remember. A good candidate is the backtick “`” (do not get confused by backticks in JavaScript for template literals and in Markdown to designate code examples / code blocks - such constructs are usually not what you try to match RegExps with).

With this you could write

'a+b'

to be interpreted as RegExp

/a\+b/

and

'a`+b'

to be interpreted as RegExp

/a+b/

and

'`^...`$'

to be interpreted as RegExp

/^\.\.\.$/

To use a backtick as a literal character you would quote it like a backslash in a RegExp by doubling it, so that e.g.

'a``+b'

would be interpreted as RegExp

/a`+b/

i.e. matching e.g.

a`b
a``b
a```b

To support flags you could allow a quoted / to the end of a jsonRegExp string followed by letters that qualify as flags, so that e.g.

'ab`/i'

would be interpreted as RegExp

/ab/i

while an unquote ‘/’ as in

'ab/i'

would be a literal / as in

/ab\/i/

You may validate the special use of ``/` by throwing an error if is used more than once or not to the end or not followed by valid flags.

Complex Regular Expressions: Beyond strings

While on the usual way literal strings may become clumsier than RegExps, the use of jsonRegExp reverses this to RegExps becoming clumsier that literal strings. This is no accident, but a direct impact of using JSON strings.

On the usual way you may find yourself pasting absurdly quoted RegExp into JSON strings (or input fields / variable to be stored as), possibly by some tooling to transform a still rather simple RegExp into a quoted version your eyes are not able to decipher. Compared to this you should be able to write jsonRegExps by hand that keep moderately complex even with contained RegExp elements.

jsonRegExp is not meant as a meta language to simplify complex RegExps, it is rather explicitly modelled on the fact that the complexity of a RegExp that qualifies to be stored in a JSON string is naturally restricted, and that accordingly restricted RegExps are expressed better by sprinkling RegExp functionality into “normal” strings than with full fledged RegExps requiring overhead to adjust them to their restricted usage.

A crucial point is when the RegExps you need are starting to get a “procedural smell”, i.e. expressing rather a complex validation process than a simple property checking.

If you have such a or an otherwise complex RegExp you should generally not try to store it in a single JSON string but e.g. as a native RegExp in a code file or function or by constructing it from easier to handle parts with JavaScript’s RegExp() constructor.

References

Mastering JS: How to Concatenate a Regular Expression

Wikipedia: Leaning toothpick syndrome

Endspacer: './markdown-assets/endspacer.png' missing - see https://hh-lohmann.github.io/html-endspacer
[top]