JSON Schema 提取必填字段

Posted

技术标签:

【中文标题】JSON Schema 提取必填字段【英文标题】:JSON Schema extract the required fields 【发布时间】:2018-06-12 09:50:04 【问题描述】:

我需要从 JSON-Schema+Data 中获取所需字段的列表。

目前,我们正在使用 AJV 在我们的带有 JSON Schema 的表单中获取错误消息,并且效果很好。

我需要一种方法来获取所有必填字段(即使已填写),以便将带有 * 的字段标记为“必填”。必填字段可能会根据架构和数据组合而变化。

还尝试破解 tv4 以提取必填字段,但未成功。

请帮忙。


此类架构的示例:


  "type": "object",
  "required": [
    "checkbox"
  ],
  "properties": 
    "checkbox": 
      "type": "boolean"
    ,
    "textbox": 
      "type": "string"
    
  ,
  "oneOf": [
    
      "required": [
        "textbox"
      ],
      "properties": 
        "checkbox": 
          "enum": [
            true
          ]
        
      
    ,
    
      "properties": 
        "checkbox": 
          "enum": [
            false
          ]
        
      
    
  ],
  "additionalProperties": false

【问题讨论】:

你的意思是你的架构中有嵌套吗?如果没有,那么模式对象应该有一个required 字段。如果您的架构是嵌套的,您可以使用 ajv 的 'getSchema()' 访问子项,然后检查它返回的内容 - 我认为您仍然可以通过这种方式获取子项的 required 字段 如果一个对象有一个必填字段,并不意味着它一定是活动的。不同的数据可以对字段(分支)给出不同的要求。例如,它的值的组合框决定是否需要另一个字段 你能举一个这样的 json 模式的例子吗?我只是好奇你如何在架构中描述它 @DanielKhoroshko 添加了。 谢谢,我知道了。从技术上讲,您可以针对架构验证一个 ampty 对象,获取所有 ajv 错误对象。每个对象都应该有.param.required,这对于必填字段来说是正确的。为了得到所有错误,不仅仅是第一个,ajv 有allErrors 选项。用户输入一些数据后,可以再次验证架构并根据错误重建一组必填字段。我相信这不是最高效的方式 【参考方案1】:

重读你的问题,做你想做的最简单的方法是

    在页面加载时获取 Json 数据, 遍历 json 数据以删除有效值(参见示例 1), 调用 tv4.validateMultiple(data, schema), 检查结果对象并获取所需字段(参见示例 2)。

样本 1

for(let prop in data) 
    if(data.hasOwnProperty(prop) 
        //set value to null, -1, or some other universally bad value
        data[prop]...value = null;
    

样本 2

let result = tv4.validateMultiple(data, schema);
let required = result.errors;

【讨论】:

我们昨天确实解决了这个问题,非常相似,只是效率更高一点。我们编辑了 tv4 以输出所需的数组,而不管数据如何。然后,每个数据更改都逐个删除,并检查是否弹出错误。我稍后会上传完整的代码 您可能想要清理它,创建一个新的 tv4 方法并将其提交回来或从 tv4 分支它。 已经这样做了 :) 如果有人需要:github.com/mikila85/tv4【参考方案2】:

我们通过以下方式解决了它:

    forking tv4(tv4 - 因为它很容易编辑):

    https://github.com/mikila85/tv4

    输出“必需”数组。

    我们迭代了每个必填字段,清空其数据并将数据+模式发送到 AJV 进行验证(AJV 而不是 tv4,因为它的解析速度更快)。

通过这样做,我们可以单独知道给定数据需要哪个必填字段。

这些是我们提出的工作功能(不是最干净的,但有助于理解)

function getAllRequiredFields() 
    var allRequiredFields = tv4.validateMultiple($scope.formModel, $scope.formSchema).requireds;
    allRequiredFields = allRequiredFields.filter(function onlyUnique(value, index, self) 
        return self.indexOf(value) === index;
    );

    return allRequiredFields;


function getRequiredFields() 
    var t0 = performance.now();

    //should be called every model change because of optimization in tv4 for the data+schema.
    var allRequiredFields = getAllRequiredFields();
    angular.forEach(allRequiredFields, function (requiredPath) 
        var modelWithDeletedRequiredProperty = angular.copy($scope.formModel);

        deleteValue(modelWithDeletedRequiredProperty, requiredPath);
        if (!validateForm(modelWithDeletedRequiredProperty)) 

            var requiredError = getErrorObjectsArray(validateForm.errors).find(function (error) 
                return error.path === requiredPath;
            );

            if (requiredError) 
                localValidation[requiredError.inputName] = localValidation[requiredError.inputName] || ;
                localValidation[requiredError.inputName].isRequired = true;
                requiredFieldsPath.push(requiredError.inputName);
            
        
    );

    var t1 = performance.now();
    console.log("form checking took " + (t1 - t0) + " milliseconds.");

【讨论】:

【参考方案3】:

这个函数递归地抓取模式索引,所以也许你可以稍微调整一下

   // https://github.com/pubkey/rxdb/blob/master/src/rx-schema.js
 export function getIndexes(jsonID, prePath = '') 
        let indexes = [];
        Object.entries(jsonID).forEach(entry => 
            const key = entry[0];
            const obj = entry[1];
            const path = key === 'properties' ? prePath : util.trimDots(prePath + '.' + key);

            if (obj.index)
                indexes.push([path]);

            if (typeof obj === 'object' && !Array.isArray(obj)) 
                const add = getIndexes(obj, path);
                indexes = indexes.concat(add);
            
        );

        if (prePath === '') 
            const addCompound = jsonID.compoundIndexes || [];
            indexes = indexes.concat(addCompound);
        

        indexes = indexes
            .filter((elem, pos, arr) => arr.indexOf(elem) === pos); // unique;
        return indexes;
    

【讨论】:

仅仅抓住必填字段是不够的(我已经可以做到了)。它必须首先使用 oneOf 分支的数据进行编译,并且只能提取活动分支。 我需要担心 null / undefined 吗? 关于上面的语句,你如何确定活跃的?您还需要哪些其他数据以及必填字段?时间不多了,但如果你延长这个问题,我可以得到一个解决方案。

以上是关于JSON Schema 提取必填字段的主要内容,如果未能解决你的问题,请参考以下文章

如何使用具有必填字段的 JSON 模式验证 http PATCH 数据

具有必填子文档字段的 Mongoose 架构

猫鼬填充未填充必填字段

Vapor Fluent 如何向现有表添加新的必填字段键

Json Schema 验证:不允许在 schema 中声明的字段以外的字段 [重复]

package.json字段简要解析