1

What is the correct way of validating a JSON document with values that contains line breaks, \n, using ajv?

Simplified example:

  • A JSON schema defines a document that has a single property called key that accepts a string value (the schema was inferred by submitting {"key":"value"} to https://jsonschema.net)
  • A JavaScript object is serialized using JSON.stringify(). As a consequence, newline characters such as \n are escaped, i.e. \\n
  • Ajv's validate() function is called to validate the serialized string

Snippet:

// JSON schema definition
const schema = {
  "definitions": {},
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "type": "object",
  "title": "The Root Schema",
  "required": [
    "key"
  ],
  "properties": {
    "key": {
      "$id": "#/properties/key",
      "type": "string",
      "title": "The Key Schema",
      "default": "",
      "examples": [
        "value"
      ],
      "pattern": "^(.*)$"
    }
  }
};

// a JavaScript object that has a line break
const data = { key: 'a string\nwith a line break' };

// serialize to JSON
const json = JSON.stringify(data);
// json === "{\"key\":\"a string\\nwith a line break\"}"

// validate
const Ajv = require('ajv');
const ajv = new Ajv();
const valid = ajv.validate(schema, json);

// print results
console.info('Valid:', valid);
console.info('Errors:', ajv.errors);

I expected this to work, but it turns out that validation fails during execution:

Valid: false
Errors: [
  {
    keyword: 'type',
    dataPath: '',
    schemaPath: '#/type',
    params: { type: 'object' },
    message: 'should be object'
  }
]

To my understanding this is because json is a string whereas the schema definition states that it should be an object.

I have also tried to deserialize the JSON string, e.g. ajv.validate(schema, JSON.parse(json));, but it also fails:

Valid: false
Errors: [
  {
    keyword: 'pattern',
    dataPath: '.key',
    schemaPath: '#/properties/key/pattern',
    params: { pattern: '^(.*)$' },
    message: 'should match pattern "^(.*)$"'
  }
]

This makes sense because JSON.parse() returns a JavaScript object which is not JSON (e.g. keys are not quoted and importantly for this question string values with unescaped \n characters).

Dependency: ajv 6.10.0

matsev
  • 32,104
  • 16
  • 121
  • 156
  • @Pointy: maybe, however I fail to see what I have done wrong. Can you please elaborate? – matsev Jun 11 '19 at 13:10
  • well I'm really not sure; I certainly don't see why that "pattern" test would fail. And it doesn't make sense that the validation library can't validate a JSON string. – Pointy Jun 11 '19 at 13:15

1 Answers1

0

I turns out that I used the wrong regex pattern. . matches any character except newline. To work around this, I changed the pattern to [\s\S], where the \s character class matches "any whitespace character" and \S character class is its negation, "any non-whitespace character". Moreover, since the pattern is defined in the value of a JavaScript object, the back slashes in \s and \S also needs to be escaped, i.e. [\\s\\S]. Consequently, the schema definition should be as follows:

const schema = {
  "definitions": {},
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "http://example.com/root.json",
  "type": "object",
  "title": "The Root Schema",
  "required": [
    "key"
  ],
  "properties": {
    "key": {
      "$id": "#/properties/key",
      "type": "string",
      "title": "The Key Schema",
      "default": "",
      "examples": [
        "value"
      ],
      "pattern": "^([\\s\\S]*)$"
    }
  }
};

See also this answer and the online regex tester

matsev
  • 32,104
  • 16
  • 121
  • 156