JSON Схема / Ajv - Как проверить объединенную длину массива строк? - PullRequest
0 голосов
/ 24 марта 2020

Допустим, мне нужно проверить адрес доставки заказа:

{ "id": 1
, "recipient": "John Doe"
, "shipping_address": [ "address line 1"
                      , "address line 2"
                      , "address line 3"
                      ]
}

Правила для адреса:

  1. Массив строк
  2. Как минимум одна строка (и может содержать столько строк, сколько необходимо)
  3. Общая длина всех строк не должна превышать X символов (скажем, 50 для примера)

Я могу реализовать 1 и 2 со следующей JSON Схемой:

{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
}

Вопрос: Как реализовать условие 3 с JSON Схема / Ajv?

1 Ответ

0 голосов
/ 24 марта 2020

Я нашел решение, используя Пользовательские ключевые слова Ajv для разработки следующей схемы:

{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}

Хитрость заключается в том, чтобы добавить поддержку maxCombinedLength в Ajv:

ajv.addKeyword("maxCombinedLength", {
  validate: (schema, data) => {
    return data.reduce((l, s) => l + s.length, 0) <= schema;
  }
});

Где:

  1. ajv является экземпляром Ajv
  2. schema является 50
  3. data является shipping_address массив

DEMO

const validate =
  ajv.compile({ type: 'array'
              , items: { type: 'string' }
              , minItems: 1
              , maxCombinedLength: 50
              });
              
console.log(
  validate([]));
// false (need at least one item)

console.log(
  validate([ "address line 1"
           , "address line 2"
           , "address line 3"
           ]));
// true

console.log(
  validate([ "address line 1"
           , "address line 2"
           , "address line 3"
           , "address line 4"
           , "address line 5"
           , "address line 6"
           , "address line 7"
           , "address line 8"           
           ])
, validate.errors);
// false (maxCombinedLength not satisfied!)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.12.0/ajv.min.js"></script>
<script>
const ajv = new Ajv;

ajv.addKeyword("maxCombinedLength", {
  validate: (schema, data, parent_schema) => {
    return data.reduce((l, s) => l + s.length, 0) <= schema;
  }
});

</script>

ПРИЛОЖЕНИЕ: Как игнорировать нестроковые массивы?

Очевидно, мы не можем использовать maxCombinedLength с массивом объектов, например.

К счастью, Ajv позволяет нам получить доступ к родительской схеме:

ajv.addKeyword("maxCombinedLength", {
  validate: (schema, data, parent_schema) => {
    if (parent_schema.items.type === 'string') {
      return data.reduce((l, s) => l + s.length, 0) <= schema;
    } else {
      return true;
    }
  }
});

То есть со следующей схемой:

{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}

Пользовательская функция ключевого слова получит 50 в качестве параметра schema, массив в качестве параметра data и полную схему в качестве параметра parent_schema.

Используется параметр parent_schema запросить тип массива. Если мы не ожидаем строки, мы можем аннулировать ключевое слово maxCombinedLength, возвращая true.

...