简体   繁体   中英

JSON Schema extract the required fields

I need to get a list of the required fields out of a JSON-Schema+Data.

Currently, we are using AJV to get error messages in our forms with JSON Schema and it is working great.

I need a way to get all the required fields (even if filled) in order to mark those fields with * as "required". required fields might change depending on the schema and data combinations.

Also tried hacking tv4 to extract the required fields without success.

Please help.


Example for such schema:

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

Rereading your question the easiest way to do what you'd like would be to

  1. get the Json data on page load,
  2. iterate over the json data to remove valid values (see sample 1 ),
  3. Call tv4.validateMultiple(data, schema),
  4. check the result object and get the required fields (see sample 2 ).

sample 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;
    }
}

sample 2

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

We solved it by:

  1. Forking tv4 (tv4 - because it was easy to edit):

    https://github.com/mikila85/tv4

    outputting an array of "Requireds".

  2. We itereted each required field, emptying it's data and sending data+schema to AJV for validation (AJV and not tv4 because its faster at parsing).

By doing that we could know individually which required field is required for the given data.

these are the working functions we came out with (not the cleanest but will help get the idea)

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.");
}

This function grabs schema indices recursively, so maybe you could adapt it a little

   // 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;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM