JSON模式/ Ajv-如何验证字符串数组的组合长度?

时间:2020-03-24 17:45:17

标签: javascript validation jsonschema ajv

假设我需要验证订单的收货地址:

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

地址规则为:

  1. 字符串数组
  2. 至少一个字符串(并且可以根据需要包含任意多个字符串)
  3. 所有字符串的总长度不得超过 X 个字符(例如50)

我可以使用以下JSON模式实现1和2:

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

问题:如何使用JSON模式/ Ajv实现条件3?

1 个答案:

答案 0 :(得分:0)

我找到了使用Ajv custom keywords设计以下架构的解决方案:

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

技巧是在Ajv中添加对maxCombinedLength的支持:

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

位置:

  1. ajv是Ajv的实例
  2. schema50
  3. datashipping_address数组

演示

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关键字无效。