func (u tdJSONUnmarshaler) resolveOp() func(json.Operator, json.Position) (interface{}, error) {
	return func(jop json.Operator, posInJSON json.Position) (interface{}, error) {
		op, exists := allOperators[jop.Name]
		if !exists {
			return nil, fmt.Errorf("unknown operator %s()", jop.Name)
		}
		if hint, exists := forbiddenOpsInJSON[jop.Name]; exists {
			if hint == "" {
				return nil, fmt.Errorf("%s() is not usable in JSON()", jop.Name)
			}
			return nil, fmt.Errorf("%s() is not usable in JSON(), use %s instead",
				jop.Name, hint)
		}
		vfn := reflect.ValueOf(op)
		tfn := vfn.Type()
		for i, p := range jop.Params {
			if ph, ok := p.(*tdJSONPlaceholder); ok {
				jop.Params[i] = ph.expectedValue.Interface()
			}
		}
		var min, max int
		addNilParam := false
		switch jop.Name {
		case "Between":
			min, max = 2, 3
			if len(jop.Params) == 3 {
				bad := false
				switch tp := jop.Params[2].(type) {
				case BoundsKind:
				case string:
					switch tp {
					case "[]", "BoundsInIn":
						jop.Params[2] = BoundsInIn
					case "[[", "BoundsInOut":
						jop.Params[2] = BoundsInOut
					case "]]", "BoundsOutIn":
						jop.Params[2] = BoundsOutIn
					case "][", "BoundsOutOut":
						jop.Params[2] = BoundsOutOut
					default:
						bad = true
					}
				default:
					bad = true
				}
				if bad {
					return nil, errors.New(`Between() bad 3rd parameter, use "[]", "[[", "]]" or "]["`)
				}
			}
		case "N", "Re":
			min, max = 1, 2
		case "SubMapOf", "SuperMapOf":
			min, max, addNilParam = 1, 1, true
		default:
			min = tfn.NumIn()
			if tfn.IsVariadic() {
				max = -1
			} else {
				max = min
			}
		}
		if len(jop.Params) < min || (max >= 0 && len(jop.Params) > max) {
			switch {
			case max < 0:
				return nil, fmt.Errorf("%s() requires at least one parameter", jop.Name)
			case max == 0:
				return nil, fmt.Errorf("%s() requires no parameters", jop.Name)
			case min == max:
				if min == 1 {
					return nil, fmt.Errorf("%s() requires only one parameter", jop.Name)
				}
				return nil, fmt.Errorf("%s() requires %d parameters", jop.Name, min)
			default:
				return nil, fmt.Errorf("%s() requires %d or %d parameters", jop.Name, min, max)
			}
		}
		var in []reflect.Value
		if len(jop.Params) > 0 {
			in = make([]reflect.Value, len(jop.Params))
			for i, p := range jop.Params {
				in[i] = reflect.ValueOf(p)
			}
			if addNilParam {
				in = append(in, reflect.ValueOf(MapEntries(nil)))
			}
			numCheck := len(in)
			if tfn.IsVariadic() {
				numCheck = tfn.NumIn() - 1
			}
			for i, p := range in[:numCheck] {
				fpt := tfn.In(i)
				if fpt.Kind() != reflect.Interface && p.Type() != fpt {
					return nil, fmt.Errorf(
						"%s() bad #%d parameter type: %s required but %s received",
						jop.Name, i+1,
						fpt, p.Type(),
					)
				}
			}
		}
		tdOp := vfn.Call(in)[0].Interface().(TestDeep)
		u.replaceLocation(tdOp, posInJSON)
		return newJSONEmbedded(tdOp), nil
	}
}
resolveOp returns a closure usable as json.ParseOpts.OpFn.
package td
import (
	"bytes"
	ejson "encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"reflect"
	"strings"
	"github.com/maxatome/go-testdeep/internal/ctxerr"
	"github.com/maxatome/go-testdeep/internal/dark"
	"github.com/maxatome/go-testdeep/internal/flat"
	"github.com/maxatome/go-testdeep/internal/json"
	"github.com/maxatome/go-testdeep/internal/location"
	"github.com/maxatome/go-testdeep/internal/types"
)
var forbiddenOpsInJSON = map[string]string{
	"Array":        "literal []",
	"Cap":          "",
	"Catch":        "",
	"Code":         "",
	"Delay":        "",
	"Isa":          "",
	"JSON":         "literal JSON",
	"Lax":          "",
	"Map":          "literal {}",
	"PPtr":         "",
	"Ptr":          "",
	"SStruct":      "",
	"Shallow":      "",
	"Slice":        "literal []",
	"Smuggle":      "",
	"String":       `literal ""`,
	"SubJSONOf":    "SubMapOf operator",
	"SuperJSONOf":  "SuperMapOf operator",
	"SuperSliceOf": "All and JSONPointer operators",
	"Struct":       "",
	"Tag":          "",
	"TruncTime":    "",
}
var jsonOpShortcuts = map[string]func() TestDeep{
	"Empty":    Empty,
	"Ignore":   Ignore,
	"NaN":      NaN,
	"Nil":      Nil,
	"NotEmpty": NotEmpty,
	"NotNaN":   NotNaN,
	"NotNil":   NotNil,
	"NotZero":  NotZero,
	"Zero":     Zero,
}
type tdJSONUnmarshaler struct {
	location.Location  
}
func newJSONUnmarshaler(pos location.Location) tdJSONUnmarshaler {
	return tdJSONUnmarshaler{
		Location: pos,
	}
}
func (u tdJSONUnmarshaler) replaceLocation(tdOp TestDeep, posInJSON json.Position) {
	newPos := u.Location
	newPos.Inside = fmt.Sprintf("%s inside operator %s ", posInJSON, u.Func)
	newPos.Func = tdOp.GetLocation().Func
	tdOp.replaceLocation(newPos)
}
func (u tdJSONUnmarshaler) unmarshal(expectedJSON interface{}, params []interface{}) (interface{}, *ctxerr.Error) {
	var (
		err error
		b   []byte
	)
	switch data := expectedJSON.(type) {
	case string:
		if strings.HasSuffix(data, ".json") {
			b, err = ioutil.ReadFile(data)
			if err != nil {
				return nil, ctxerr.OpBad(u.Func, "JSON file %s cannot be read: %s", data, err)
			}
			break
		}
		b = []byte(data)
	case []byte:
		b = data
	case io.Reader:
		b, err = ioutil.ReadAll(data)
		if err != nil {
			return nil, ctxerr.OpBad(u.Func, "JSON read error: %s", err)
		}
	default:
		return nil, ctxerr.OpBadUsage(
			u.Func, "(STRING_JSON|STRING_FILENAME|[]byte|io.Reader, ...)",
			expectedJSON, 1, false)
	}
	params = flat.Interfaces(params...)
	var byTag map[string]interface{}
	for i, p := range params {
		switch op := p.(type) {
		case *tdTag:
			if byTag[op.tag] != nil {
				return nil, ctxerr.OpBad(u.Func, `2 params have the same tag "%s"`, op.tag)
			}
			if byTag == nil {
				byTag = map[string]interface{}{}
			}
			p = nil
			if op.expectedValue.IsValid() {
				p = op.expectedValue.Interface()
			}
			byTag[op.tag] = newJSONNamedPlaceholder(op.tag, p)
		default:
			params[i] = newJSONNumPlaceholder(uint64(i+1), p)
		}
	}
	final, err := json.Parse(b, json.ParseOpts{
		Placeholders:       params,
		PlaceholdersByName: byTag,
		OpShortcutFn:       u.resolveOpShortcut(),
		OpFn:               u.resolveOp(),
	})
	if err != nil {
		return nil, ctxerr.OpBad(u.Func, "JSON unmarshal error: %s", err)
	}
	return final, nil
}