I'm writing a function that simply loops through a Json schema. Let us say we have a simple json schema such as:
var foo = {
"description": "A car schema",
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"_rev": {
"type": "string"
},
"sources": {
"type": "object",
"properties": {
"mam": {
"type": "object",
"properties": {
"source_id": {
"type": [ "integer", "null" ]
},
"SOR": {
"type": ["boolean","null"]
}
}
},
"sas": {
"type": "object",
"properties": {
"source_id": {
"type": "string"
},
"SOR": {
"type": ["boolean","null"]
},
"CAR": {
"type": ["object","null"]
}
}
}
}
}
}
}
We're trying to collect the type of the key object from it. Here is function search for CAR type should return => "object"
parseObjectProperties = (obj)=> {
for (var k in obj) {
if(k === "CAR"){
console.log(_.values(obj[k])[0][0]) // When I do a console log like this prin the object value
return _.values(obj[k])[0][0] // but in return i get undefined
}
if ( _.isObject( obj[k]) && !_.isNil(obj[k])) {
return parseObjectProperties(obj[k])
}
}
}
parseObjectProperties(foo);
When I run it, the inner console.log(_.values(obj[k])[0][0]) shows the correct value: object
but if i run it
console.log(parseObjectProperties(foo));
I get
undefined
Why isn't the function properly returning the correct value => "object"?
Thank you!
I just re-wrote this as I didn't understand the logic you were using. Does this solve your problem?
FYI your object CAR, has a property named type whose value is an array that contains 2 values, "object" and "null" so you want to use obj[k].type[0] if you want to get "object" as the result.
const parseObjectProperties = (obj) => {
var result = null;
for (var k in obj)
if (k == "CAR") return obj[k].type[0];
else if (obj[k] instanceof Object)
result = parseObjectProperties(obj[k]);
return result;
}
var foo = {
"description": "A car schema",
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"_rev": {
"type": "string"
},
"sources": {
"type": "object",
"properties": {
"mam": {
"type": "object",
"properties": {
"source_id": {
"type": ["integer", "null"]
},
"SOR": {
"type": ["boolean", "null"]
}
}
},
"sas": {
"type": "object",
"properties": {
"source_id": {
"type": "string"
},
"SOR": {
"type": ["boolean", "null"]
},
"CAR": {
"type": ["object", "null"]
}
}
}
}
}
}
}
console.log(parseObjectProperties(foo));
Related
I have a JSON like so:
{
"parent": {
"type": "Object",
"value": {
"childName": { "type": "String", "value": "A string" }
}
}
}
Pretty much, the pattern is parent has type and value, but I want the value of parent to be value
{
"parent": {
"childName": "A string"
}
}
How can I set the parent's value to be the child named value recursively in JavaScript?
The main issue I am having is doing this recursively for a very large file.
Examples:
The start value of Level is `{ "type": "string", "value": "A string" }
I want to make the value of Level become "A String", making the end value of Level become "A String"
The start value of parentObject is { "type": "Object", "value": { "anotherObject": { "type": "string", "value": "Another string" }, "secondObject": { "type": "string", "value": "second string" } } }
I want to make the value of parentObject become { "anotherObject": { "type": "string", "value": "Another string" }, "secondObject": { "type": "string", "value": "second string" } }
And the value of anotherObject become "Another string"
Making the final result
{"parentObject": { "anotherObject": "Another string" }, { "secondObject": "second string" }}
Here is a example JSON file
The first thing is the object should be symmetric if your want to do it in a recursive way.
Example:
const input = {
"parentObject": {
"type": "Object",
"value": {
"anotherObject": {
"type": "string",
"value": "Another string"
}
}
}
};
The recursive function is something like this.
const input = {
"parentObject": {
"type": "Object",
"value": {
"anotherObject1": {
"type": "string",
"value": "Another string"
},
"anotherObject2": {
"type": "string",
"value": "Another string"
}
}
}
};
const recursivefn = (obj) => {
const keys = Object.keys(obj);
let acc = {}
keys.forEach((key)=>{
if (typeof obj[key].value === 'object') {
acc = { ...acc, [key]: recursivefn(obj[key].value) };
} else {
acc = { ...acc, [key]: obj[key].value};
}
});
return acc;
}
console.log(recursivefn(input));
I want to add conditionally required based on value of some other property.
'companyName' and 'companyAddress' should be required only if 'isInexperienced'
value is false.
Schema
{
"type": "object",
"properties": {
"previous_employment_section": {
"type": "array",
"items": {
"type": "object",
"properties": {
"companyAddress": {
"type": "string"
},
"companyName": {
"type": "string"
}
},
"if": {
"#/properties/isInexperienced": {
"const": false
}
},
"then": {
"required": [
"companyName",
"companyAddress"
]
}
}
},
"isInexperienced": {
"type": "boolean"
}
}
}
Data
{
"previous_employment_section": [],
"isInexperienced": true
}
I do not fully understand the intention of your original schema, but how about this one?
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"previous_employment_section": {
"type": "array",
"items": {
"type": "object",
"properties": {
"companyAddress": {
"type": "string"
},
"companyName": {
"type": "string"
}
}
}
},
"isInexperienced": {
"type": "boolean"
}
},
"if": {
"properties": {
"isInexperienced": {
"const": false
}
}
},
"then": {
"properties": {
"previous_employment_section": {
"items": {
"required": [
"companyName",
"companyAddress"
]
},
"minItems": 1
}
}
}
}
It is not possible. We need to have “if” on a higher level, “properties” can be nested. Leadpony's method can be used.
I have a unique data that I am attempting to validate:
{
"name": "Some random name",
"blocks": [
{"cj5458hyl0001zss42td3waww": {
"quantity": 9,
"rate": 356.77,
"dId": "ewdwe4434"
}},
{"cj5458hyl0001zss42td3wawu": {
"quantity": 4,
"rate": 356.77,
"dId": "3434ewdwe4434"
}}]
}
Here is the composition that I have right now (invalid and incorrect):
const subSchema = {
"type": ["string"],
"pattern": "/^c[^\s-]{8,}$/",
"properties": {
"quantity": {
"type": "integer"
},
"rate": {
"type": "integer"
},
"dId": {
"type": "string"
}
},
"required": ["quantity", "rate", "dId"]
};
const schema = {
"type": ["object"],
"properties": {
"name": {
"type": "string"
},
"blocks": {
"type": "array",
"items": subSchema,
"uniqueItems": true,
"minItems": 1
}
},
"required": ["name", "blocks"]
};
and how I am validating it (for context):
const { BadRequestError } = require("restify");
const ajv = require("ajv");
var schemaValidator = ajv();
const validateRoomTypePostRequest = (req, res, next) => {
if (req.body && req.body.data){
const blockReq = Object.assign({}, req.body.data);
const testSchemaValidator = schemaValidator.compile(schema);
const valid = testSchemaValidator(blockReq);
if (!valid) {
const messages = testSchemaValidator.errors.map(e => {
return e.message;
});
return next(new BadRequestError(JSON.stringify(messages)));
}
return next();
}
else {
return next(new BadRequestError("Invalid or non-existent request body"));
}
};
Here is what I have referenced so far:
1) AJV schema validation for array of objects
2) https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md
3) https://spacetelescope.github.io/understanding-json-schema/reference/object.html
Additional information:
1) Using Node 8.1.3
2) AJV version 5.2
I know that I need to use an array of items to describe the object. However the object contains a unique cuid as a key and the value as a an object. I would like to understand how to describe this data using such a schema that validates the nested properties and the cuid. I welcome feedback on how to best approach this data. Thank you for your time.
I did some soul searching and realized that all I had to do was leverage the patternPropertieskeyword, specific to strings.
{
"blockType": {
"additionalProperties": false,
"type": "object",
"properties": {
"name": {
"type": "string"
},
"blocks": {
"type": "array",
"items": {
"type": "object",
"patternProperties": {
"^[a-z][a-z0-9\\-]*$": {
"type": ["object"],
"properties": {
"rate": {
"type": ["integer"]
},
"quantity": {
"type": ["integer"]
},
"dId": {
"type": "string"
}
},
"additionalProperties": false,
"required": ["dId", "quantity", "rate"]
}
}
}
}
},
"required": ["name", "blocks"]
}
}
I could improve the regex for validating the cuid.
I'm trying to navigate through a complex nested JSON, however my attempt didn't get me too far as it keeps returning me the last index JSON.
This is how my Objects looks like, and trying to navigate through it and getting other objs/schemas that are in $ref.
Raw JSON
{
"type": "object",
"properties": {
"Id": {
"format": "int32",
"type": "integer"
},
"Status": {
"enum": [
"Preparing",
"AwaitingCompletion",
"Cancelled",
"Completed"
],
"type": "string"
},
"ExternalReference": {
"type": "string"
},
"Customer": {
"$ref": "#/definitions/Customer"
},
"OrderLineGroups": {
"type": "array",
"items": {
"$ref": "#/definitions/OrderLineGroup"
}
},
"Promotions": {
"type": "array",
"items": {
"$ref": "#/definitions/PromotionSummary"
}
},
"OriginatingSite": {
"type": "object",
"properties": {
"Id": {
"format": "int32",
"type": "integer"
},
"PropertyCode": {
"type": "string"
},
"StoreCode": {
"type": "string"
}
}
},
"CustomData": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "object"
}
}
}
}
}
In my code I have done for() and hasOwnProperty(), however my problem is that it doesn't give me all the JSON back even the condition is met (e.g if there's no type property), it only gives the last index or object that doesn't have type property. Also doesn't return me any of the objects if type property is array.
// Get property of #/definitions/obj
let prop = apiDefinition[splitResponse.split('/')[2]].properties;
console.log([prop])
var s = [apiDefinition[splitResponse.split('/')[2]].properties];
// Transform JS Object of #/definitions/Obj to JSON
var parentJSON = JSON.stringify(apiDefinition[splitResponse.split('/')[2]]);
for (var x in prop) {
if (prop.hasOwnProperty(x)) {
if (prop[x].type && prop[x].type === 'array') {
console.log('All type Array >> ', x);
let objKeyProp = apiDefinition[prop[x].items.$ref.split('/')[2]];
let objJsonStringified = JSON.stringify(objKeyProp);
let refString = '{"$ref"'+':' + '"' + prop[x].items.$ref + '"}';
this.compiledJson = JSON.parse(parentJSON.replace(refString, objJsonStringified));
} else if (!prop[x].type) {
console.log('all arrays >> ', x)
let objKeyProp = apiDefinition[prop[x].$ref.split('/')[2]];
let objJsonStringified = JSON.stringify(objKeyProp);
let refString = '{"$ref"'+':' + '"' + prop[x].$ref + '"}';
this.compiledJson = JSON.parse(parentJSON.replace(refString, objJsonStringified));
}
}
}
I have a very large json schema which I would like to display in an Angular Tree Control. However, the json schema is not exactly in the treemodel format supported by the Angular Tree Control. For example, the children (properties) in the schema are not in an array. How would I go about converting the json schema into a treemodel format?
The schema looks something like this (except more complicated with up to 10 levels of nesting):
{
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"address": {
"type": "object",
"properties": {
"addressLine1": {
"type": "string"
},
"addressLine2": {
"type": "string"
}
}
}
}
}
For it to render correctly in the Angular Tree Control, it needs to look like this:
{
"type": "object",
"properties": [
{
"name": "firstName",
"type": "string"
},
{
"name": "lastName",
"type": "string"
},
{
"name": "address",
"type": "object",
"properties": [
{
"name": "addressLine1",
"type": "string"
},
{
"name": "addressLine2",
"type": "string"
}
]
}
]
}
This is an example, can be refactored to be recursive to deeper levels.
var data = {
"type": "object",
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"address": {
"type": "object",
"properties": {
"addressLine1": {
"type": "string"
},
"addressLine2": {
"type": "string"
}
}
}
}
};
function toTreeModel(obj){
var treeModel = {};
for (var a in obj) {
if(a === 'properties'){
treeModel[a] = []
var i = 0;
var e = 0;
for(b in obj[a]){
if(obj[a][b].type === 'object'){
treeModel[a][i] = {name: b, type: obj[a][b].type, properties: []};
for(c in obj[a][b].properties){
treeModel[a][i].properties.push({name: c, type: obj[a][b].properties[c].type});
e++;
}
} else {
treeModel[a][i] = {name: b, type: obj[a][b].type};
}
i++;
}
} else {
treeModel[a] = obj[a];
}
}
return treeModel;
}
var toTree = toTreeModel(data);
// console.log(toTree);
document.getElementById("output").innerHTML = JSON.stringify(toTree, undefined, 2);
<pre id="output">
</pre>
it support nested also
var data = {
"type": "object",
"properties": {
"checked": {
"type": "boolean",
},
"dimensions": {
"type": "object",
"properties": {
"width": {
"type": "integer",
},
"height": {
"type": "integer",
},
"volume": {
"type": "object",
"properties": {
"length": {
"type":"integer",
},
"breadth":{
"type": "integer"
}
}
}
}
},
"id": {
"type": "integer",
},
"name": {
"type": "string",
},
"price": {
"type": "number",
}
}
}
function findProperties(obj){
let properties = []
for(key in obj){
if(obj[key].properties){
properties.push({name: key, datatype: obj[key].type, properties: findProperties(obj[key].properties)})
}else{
properties.push({ name: key, datatype: obj[key].type});
}
}
return properties;
}
function findData(data){
let result = "";
for(key in data){
if(key == 'properties'){
result = findProperties(data.properties)
}
}
return result;
}
console.log(JSON.stringify(findData(data)));