jsonschema icon indicating copy to clipboard operation
jsonschema copied to clipboard

Allow regular expression provider to be changed

Open tschaub opened this issue 3 years ago • 0 comments

Since regular expressions used in JSON schema docs aren't restricted to the RE2 syntax, it would be ideal if the validator could be configured to use something other than the regexp package.

This pull request suggests making it possible to configure the regular expression provider used by the validator. New exported names:

  • jsonschema.Regexp - an interface for working with regular expressions
  • jsonschema.RegexpProvider - a function type for supplying a regular expression
  • jsonschema.SetRegexpProvider - sets the default regular expression provider

With these changes, here is an example of how you could use the github.com/dlclark/regexp2 package to parse ECMAScript-flavor regex (the flavor used in JSON Schema):

package main

import (
	"encoding/json"
	"log"
	"time"

	"github.com/dlclark/regexp2"
	"github.com/santhosh-tekuri/jsonschema/v5"
)

func init() {
	// configure the default regexp provider
	jsonschema.SetRegexpProvider(func() jsonschema.Regexp {
		return &ecmaRegexp{}
	})
}

type ecmaRegexp struct {
	re *regexp2.Regexp
}

// ecmaRegexp implements the jsonschema.Regexp interface
var _ jsonschema.Regexp = (*ecmaRegexp)(nil)

func (r *ecmaRegexp) Compile(expr string) error {
	re, err := regexp2.Compile(expr, regexp2.ECMAScript)
	if err != nil {
		return err
	}
	re.MatchTimeout = time.Second
	r.re = re
	return nil
}

func (r *ecmaRegexp) MustCompile(expr string) {
	re := regexp2.MustCompile(expr, regexp2.ECMAScript)
	re.MatchTimeout = time.Second
	r.re = re
}

func (r *ecmaRegexp) MatchString(s string) bool {
	match, _ := r.re.MatchString(s)
	return match
}

func (r *ecmaRegexp) String() string {
	return r.re.String()
}

func main() {
	// schema with a negative lookahead
	// allowed in JSON schema but not the golang regexp package
	schema, err := jsonschema.CompileString("schema.json", `
		{
			"type": "string",
			"pattern": "^(?!foo)" 
		}
	`)
	if err != nil {
		log.Fatal(err)
	}

	var v interface{}
	if err := json.Unmarshal([]byte(`"bar"`), &v); err != nil {
		log.Fatal(err)
	}

	if err = schema.Validate(v); err != nil {
		log.Fatalf("%#v\n", err)
	}
}

Alternatively, the github.com/dlclark/regexp2 package could be used by default (see #60).

Fixes #31.

tschaub avatar Mar 30 '22 19:03 tschaub