首页 > 解决方案 > 如何使用 Joi 验证具有相同模式但没有限制的嵌套对象

问题描述

假设我在下面的这种模式中有一个嵌套对象,我想用它来验证它Joi

{
    "AND": [
        { "key": "", "value": "" },
        { "key": "", "value": "" },
        { "OR": [
            { "key": "", "value": "" },
            { "key": "", "value": "" },
            { "AND": [
                    { "key": "", "value": "" },
                    { "key": "", "value": "" },
                    ...
                ]
            },
            ...
        ],
        ...
    ]
}

这是我的 V.1,它只允许 1 级深度:

const obj = Joi.object().keys({
  key: Joi.string(),
  value: Joi.string(),
});
obj.keys({
  AND: Joi.array().items(obj),
  OR: Joi.array().items(obj),
}).nand('AND', 'OR');

const nested = Joi.object()
  .keys({
    AND: Joi.array().items(obj),
    OR: Joi.array().items(obj),
  })
  .nand('AND', 'OR');

这里的 V.2 只允许 2 级深度:

  const obj = Joi.object().keys({
    key: Joi.string(),
    value: Joi.string(),
    AND: Joi.array().items(Joi.object({ key: Joi.string(), value: Joi.string() })),
    OR: Joi.array().items(Joi.object({ key: Joi.string(), value: Joi.string() })),
  });
  
  const nested = Joi.object()
    .keys({
      AND: Joi.array().items(obj),
      OR: Joi.array().items(obj),
    })
    .nand('AND', 'OR');

有人可以建议我正确的方法吗?

谢谢

标签: javascriptvalidationrecursionjoi

解决方案


Both of your attempts are a great start but as you've found it's the recursive nature of the pattern that's tricky to deal with.

Thankfully Joi has a solution to this with .link():

Links to another schema node and reuses it for validation, typically for creative recursive schemas...

We can also make use of .pattern() to avoid having to duplicate our rules for AND and OR.

I've found the following schema works:

// attempt to match one of these schemas
Joi.alternatives([
    // an object containing the key AND or OR where it's value matches 
    // this entire schema definition
    Joi.object().pattern(/^(AND|OR)$/, Joi.array().items(Joi.link('#schema'))),
    // an object containing a key/value pair
    Joi.object().keys({
        key: Joi.string(),
        value: Joi.string()
    })
// we identify our schema with an ID so we can reference it in Joi.link()
]).id('schema')

With test data:

{
    "AND": [
        { "key": "", "value": "" },
        { "key": "", "value": "" },
        {
            "OR": [
                { "key": "", "value": "" },
                { "key": "", "value": "" },
                {
                    "AND": [
                        { "key": "", "value": "" },
                        { "key": "", "value": "" }
                    ]
                }
            ]
        }
    ]
}

You can test it out here.

--

For those using a Joi version < v16 that doesn't support link(), you can use lazy() instead.

const schema = Joi.alternatives([
    Joi.object().pattern(/^(AND|OR)$/, Joi.array().items(Joi.lazy(() => schema))),
    Joi.object().keys({
        key: Joi.string(),
        value: Joi.string()
    })
]);

推荐阅读