json-editor: errors not work in anyOf

General information

  • json-editor version: 1.4 & 2.0 /* all */

Expected behavior

when error occur the editor can show error message.

Actual behavior

It doesn’t show error message in anyOf array.

Steps to reproduce the behavior

Direct link to example: https://is.gd/FM9u4L

labelComponent doesn’t work.

{
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_]+$",
      "minLength": 1,
      "maxLength": 50
    },
    "children": {
      "items": {
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/definitions/labelComponent"
          }
        ]
      },
      "type": "array"
    }
  },
  "type": "object",
  "defaultProperties": [
    "id",
    "children"
  ],
  "definitions": {
    "labelComponent": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[a-zA-Z0-9_]+$",
          "minLength": 1,
          "maxLength": 50
        }
      },
      "defaultProperties": [
        "id"
      ]
    }
  }
}

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 27 (10 by maintainers)

Commits related to this issue

Most upvoted comments

I am probably missing something. If I open the original link https://is.gd/FM9u4L , it still does not work as expected: there is only “high-level” error “Value must validate against at least one of the provided schemas”, but no errors inside anyOf are displayed. The same behavior is in my local installation of this editor (by npm).

How can errors inside anyOf options be displayed?

@pmk65 Yes i think so.

It would be great if someone could create a page in tests/pages with the schema.

@wclssdn Sorry, It looks like your #445 PR have only been merged into the master branch. I have asked @schmunk42 to add it to the develop/2.x branch too.

@wclssdn I have extended the validation function, so now the error object has a count (If larger than 1, then duplicate errors was found/removed.)

Example error object with errorcount

[
  {
    "path": "root.ports",
    "property": "pattern",
    "message": "Value must match the pattern ^[ ]*[1-9][0-9]{0,4}([ ]*-[ ]*[1-9][0-9]{0,4})?([ ]*,[ ]*[1-9][0-9]{0,4}([ ]*-[ ]*[1-9][0-9]{0,4})?)*[ ]*$",
    "errorcount": 1
  },
  {
    "path": "root",
    "property": "not",
    "message": "Value must not validate against the provided schema",
    "errorcount": 2
  }
]

https://github.com/json-editor/json-editor/blob/009f8fe5aef2e90403b44730ba544107b1aa4b8d/src/validator.js#L670-L691

Then In the editor showValidationErrors function, we can add something like:
if (error.errorcount && error.errorcount > 1) error.message += ' (' + error.errorcount + ' errors)'; (I have added it to src/editors/object.jsin the develop/2.x branch) This would produce an error message like this: Value must not validate against the provided schema (2 errors)

I have added this to the _validateSchema in src/validators.js to remove any duplicate error messages. (Any error object with same path, message and property will be filtered out.)

    // Filter out duplicate error messages
    errors = errors.filter(function (value, i, self) {
        return self.findIndex(function (test) {
            return test.path === value.path && test.message === value.message && test.property === value.property;
        }) === i;
    });

I think the if statement changes are correct, and the position above the field is also correct. As the error is:

  {
    "path": "root.children.0",
    "property": "anyOf",
    "message": "Value must validate against at least one of the provided schemas"
  }

So it makes sense that it is place above the fields. (at the root.children.0 block)

we might need 2 loops. one for the root.children.0 and one for the children root.children.0.id

The errors of anyOf is already checked in src/validator.js#L90. So the second loop is not necessary.

I have added the switched if statement to the develop/2.x branch.

@wclssdn The check variable in src/editors/multiple.js, line 297 expands to root.children.0.anyOf[0], but error.path is root.children.0, so they will never match.

Changing the if statement from; if(error.path.substr(0,check.length)===check) { To: if(error.path === check.substr(0,error.path.length)) {

Will match root.children.0 against root.children.0, so the match is valid.

BUT the fields data-schemapath is root.children.0.id, so the error is not placed under the field, but at the top. (So we need to figure out how to append the property name (in this case id, to the error path)

I think it needs to loop through the child editors in the compare function. That way you can get the property name. Here’s my test so far. Now the error is placed under the field. but there’s a problem if there more than 1 property. So it’s not entirely correct.

  showValidationErrors: function(errors) {
    var self = this;

    // oneOf and anyOf error paths need to remove the oneOf[i] part before passing to child editors
    if(this.oneOf || this.anyOf) {
      var check_part = this.oneOf? 'oneOf' : 'anyOf';

      $each(this.editors,function(i,editor) {
        if(!editor) return;
        var new_errors = [];
        $each(editor.editors,function(i,editor) {

          var check = self.path+'.'+check_part+'['+i+']';

          $each(errors, function(j,error) {
            if(check.substr(0,error.path.length)===error.path) {
              var new_error = $extend({},error);
              new_error.path = self.path+new_error.path.substr(check.length) + '.' +check.replace(/(.*\[)(.*)(\])$/ig, "$2");
              new_errors.push(new_error);
            }
          });
        });
        editor.showValidationErrors(new_errors);
      });
    }
    else {
      $each(this.editors,function(type,editor) {
        if(!editor) return;
        editor.showValidationErrors(errors);
      });
    }
  }

@wclssdn I noticed a similar problem in the src/editors/multiselect.js editor, where the errors wasn’t displayed on form. This is because the error for child elements have a number added to the end of the path. I.e. root.children.1

[
  {
    "path": "root.children.1",
    "property": "anyOf",
    "message": "Value must validate against at least one of the provided schemas"
  }
]

In the src/editors/multiselect.js editor, I solved that by creating a regex to match against the path (which allows the additional child number on the path) instead of a direct comparison. Like this:

  showValidationErrors: function(errors) {
    var regexPath = new RegExp('^' + this.escapeRegExp(this.path) + '(\\.\\d+)?$'),
        messages = [];

    $each(errors, function(i,error) {
      if (error.path.match(regexPath)) {
        messages.push(error.message);
      }
    });

    if (messages.length) {
      this.theme.addInputError(this.input || this.inputs, messages.join('. ') + '.');
    }
    else {
      this.theme.removeInputError(this.input || this.inputs);
    }
  }