I have several JSON files that look like this
{
"$schema": "someURL",
"id": "someURL",
"type": "object",
"properties": {
"copyright": {
"id": "someURL",
"type": "object",
"description": "Setup for copyright link",
"properties": {
"translation": {
"id": "someURL",
"type": "string",
"description": "someString"
},
"url": {
"id": "someURL",
"type": "string",
"description": "someString"
}
}...
what I need to do is add a removable and appendable attribute to each item inside every instance of properties and set them to true. so the output should look like this:
{
"$schema": "someURL",
"id": "someURL",
"type": "object",
"properties": {
"copyright": {
"removable": true,
"appendable": true,
"id": "someURL",
"type": "object",
"description": "Setup for copyright link",
"properties": {
"translation": {
"removable": true,
"appendable": true,
"id": "someURL",
"type": "string",
"description": "someString"
},
"url": {
"removable": true,
"appendable": true,
"id": "someURL",
"type": "string",
"description": "someString"
}
}...
Is there a way to automate this? as in write a script that automatically adds these fields right below each item in properties?
using NodeJS I would write something like this:
const fs = require('fs');
let args = process.argv.map(val=>val));
let contents = fs.readFileSync(args[0]);
let obj = JSON.parse(contents);
const proc = (obj, append)=>{
for (prop in obj)
{
let p = obj[prop];
if (p.constructor!=Object)
continue;
if (append)
{
p.removable = true;
p.appendable = true;
}
proc(p, prop=='properties');
}
};
console.log(JSON.stringify(proc(obj), null, 2));
And then in bash do:
find -name '*\.json' -exec 'node proc.js {} > {}.new'
I didn't test anything, everything is from my head, but for starting point should be good enough.
Given that you want to apply the update to all objects that have a key named "properties", wherever they occur, I'd be inclined to use walk/1. In any case, to make things clearer and maybe easier, it will be helpful to define a helper function that will apply the update if the input is of the right type:
def update(obj):
if type == "object" and has("properties")
then .properties |= with_entries( .value += obj )
else .
end;
Using walk/1, the solution is now trivial:
walk( update({removable: true, appendable: true}) )
Robustification
It might be prudent to change the "then" line above to:
then .properties |=
with_entries( if .value | type == "object"
then .value += obj
else . end)
Related
I am using the AJV package in my node.js project.
I am trying to validate some data against a couple of schema files. Both of these schema files are in the same directory:
/dir
|
parent_schema.json
|
sub_schema.json
/data
|
data.json
I am trying to get a super simple example of the $ref property working but I am having trouble. parent_schema.json looks like:
{
"properties": {
"foo": { "type": "string" },
"bar": { "$ref": "sub_schema.json" }
}
}
And sub_schema.json looks like:
{
"properties": {
"sub1": { "type": "string" },
}
}
And I am trying to validate my data.json which for the sake of completeness looks like:
{
"foo": "whatever",
"bar": {
"sub1": "sometext"
}
}
The issue I'm having is with my $ref path. I am getting this error from AJV:
MissingRefError {
message: "can't resolve reference subschema1.json from id #"
missingRef: "subschema1.json"
missingSchema: "subschema1.json"
}
Anyone see what's wrong with my path? I know you are also supposed to use the # to select what specific property you want matched against, but I want the ENTIRE schema to be used.
It's a common misconception that $ref somehow "loads" a file.
See what ajv.js.org says:
$ref is resolved as the uri-reference using schema $id as the base URI (see the example).
And:
You don’t have to host your schema files at the URIs that you use as schema $id. These URIs are only used to identify the schemas, and according to JSON Schema specification validators should not expect to be able to download the schemas from these URIs.
Ajv won't try loading this schema from stack://over.flow/string for example:
{
"$id": "stack://over.flow/string",
"type": "string"
}
If you want to reference that schema in another schema, they both need to have the same base URI stack://over.flow/ e.g.,
{
"$id": "stack://over.flow/object",
"type": "object",
"properties": {
"a": { "$ref": "string#" }
}
}
Here { "$ref": "string#" } says "import the schema at stack://over.flow/string" so you end up with:
{
"$id": "stack://over.flow/object",
"type": "object",
"properties": {
"a": {
"$id": "stack://over.flow/string",
"type": "string"
}
}
}
This allows you to combine small schemas:
const ajv = new Ajv;
ajv.addSchema({
"$id": "stack://over.flow/string",
"type": "string"
});
ajv.addSchema({
"$id": "stack://over.flow/number",
"type": "number"
});
const is_string = ajv.getSchema("stack://over.flow/string");
const is_number = ajv.getSchema("stack://over.flow/number");
console.log(is_string('aaa'), is_string(42));
console.log(is_number('aaa'), is_number(42));
const is_ab = ajv.compile({
"$id": "stack://over.flow/object",
"type": "object",
"properties": {
"a": { "$ref": "string#" },
"b": { "$ref": "number#" }
}
});
console.log(is_ab({a: "aaa", b: 42}));
console.log(is_ab({a: 42, b: "aaa"}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.12.2/ajv.min.js"></script>
(Please note that in your example both schemas are incorrect. You're missing {"type": "object"} in both.)
To answer your question:
const ajv = new Ajv;
ajv.addSchema({
"$id": "stack://over.flow/parent.schema",
"type": "object",
"properties": {
"foo": { "type": "string" },
"bar": { "$ref": "child.schema#" }
}
});
ajv.addSchema({
"$id": "stack://over.flow/child.schema",
"type": "object",
"properties": {
"sub1": { "type": "string" },
}
});
const is_parent = ajv.getSchema("stack://over.flow/parent.schema");
const is_child = ajv.getSchema("stack://over.flow/child.schema");
console.log(is_parent({
"foo": "whatever",
"bar": {
"sub1": "sometext"
}
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.12.2/ajv.min.js"></script>
I'm working on creating my own schematics. This schematics will be responsible for creating a component (container) with some code. Template of this component will contain a few other components. One of this component will be banner component that will be optional. This banner will display text that will be translated into other languages, so I also should ask the user to provide (default) translation text if the banner will be included in the component.
Here is an example of this template:
name#dasherize.component.html.template:
<% if (includeBanner) { %>
<app-banner [title]="'<%= translationModuleKey %>.banner.title' | translate"
[description]="'<%= translationModuleKey %>.banner.description' | translate">
</app-banner>
<% } %>
<app-other-component>
</app-other-component>
Here is my schema.json:
{
"$schema": "http://json-schema.org/schema",
"id": "MySchematics",
"title": "My schematics",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the container",
"x-prompt": "Container name"
},
"includeBanner": {
"type": "boolean",
"description": "Include banner",
"default": "true",
"x-prompt": "Include banner"
},
"bannerTitle": {
"type": "string",
"description": "Banner title",
"x-prompt": "Banner title"
},
"bannerDescription": {
"type": "string",
"description": "Banner description",
"x-prompt": "Banner description"
},
"translationModuleKey": {
"type": "string",
"description": "Root key for translations"
}
},
"required": [
"name", "includeBanner", "bannerTitle", "bannerDescription"
]
}
My problem is that when user will set includeBanner to true, fields bannerTitle and bannerDescription should be required and there should be displayed prompt if those properties were not provided, but if includeBanner will be false, bannerTitle and bannerDescription shouldn't be required and there shouldn't be displayed prompt to fill these properties if those properties were not provided.
Any idea how to achieve that?
I was struggling with the same problem. What I've discovered - if you need conditional prompts, then you can't rely on declarative schema.json file (it doesn't support conditions).
Instead, you should use the askConfirmation function from #angular/cli/utilities/prompt.
So your example could look like this:
import { askConfirmation } from '#angular/cli/utilities/prompt';
export function yourSchematicsFn(options: Schema): Rule {
return async (tree: Tree, context: SchematicContext) => {
const includeBanner = await askConfirmation('Include Banner?', true);
if(includeBanner) {
// ask for bannerTitle and bannerDescription
}
else {
// do something else
}
return chain(/* chain your rules here */);
}
}
I've discovered this in Angular CLI ng-add schematics source code: askConfirmation.
I need to remove an object from an JSON tree. I know a reference to that object. Is there a nice way to do it via JavaScript or jQuery besides traversing the whole tree?
Example:
party = {
"uuid": "4D326531-3C67-4CD2-95F4-D1708CE6C7A8",
"link": {
"rel": "self",
"href": "http://localhost:8080/cim/party/4D326531-3C67-4CD2-95F4-D1708CE6C7A8"
},
"type": "PERSON",
"name": "John Doe",
"properties": {
"CONTACT": [
{
"category": "CONTACT",
"type": "EMAIL",
"key": "email",
"value": "john.doe#doe.at",
"id": "27DDFF6E-5235-46BF-A349-67BEC92D6DAD"
},
{
"category": "CONTACT",
"type": "PHONE",
"key": "mobile",
"value": "+43 999 999990 3999",
"id": "6FDAA4C6-9340-4F11-9118-F0BC514B0D77"
}
],
"CLIENT_DATA": [
{
"category": "CLIENT_DATA",
"type": "TYPE",
"key": "client_type",
"value": "private",
"id": "65697515-43A0-4D80-AE90-F13F347A6E68"
}
]
},
"links": []
}
And i have a reference: contact = party.properties.contact[1]. And I want to do something like delete contact.
You may delete it this way. I just tested it.
var party = {
// ...
}
alert(party.properties.CONTACT[0]) // object Object
delete party.properties.CONTACT[0] // true
alert(party.properties.CONTACT[0]) // undefined
Fiddle
UPDATE
In the case above party is a direct property of window object
window.hasOwnProperty('party'); // true
and that's why you can't delete a property by reference. Anyhow, behavior of delete operator with host objects is unpredictable. Though, you may create a scope around the party object and then you'll be allowed to delete it.
var _scope = {};
var _scope.party = {
// ...
};
var r = _scope.party.properties.CONTACT[0];
window.hasOwnProperty('party'); // false
alert(r) // object Object
delete r // true
alert(r) // undefined
It only works one way: a variable holds a reference, but there is no way given a particular reference to infer what variables hold it (without iterating over them and comparing).
I am trying to validate JSON scheme using TV4.
My validation is using hierarchical JSON and is based on this basic example:
var data = {
"foo": "bar"
};
var schema = {
"type": "object",
"properties": {
"foo": {
"type": "string"
}
},
"required": ["foo"]
};
var result = tv4.validateResult(data, schema);
In my test I want to add one more hierarchy level:
var data = {
"foo": {
"test": "bar"
}
};
var schema = {
"type": "object",
"properties": {
"foo": {
"test": {
"type": "string"
}
}
},
"required": ["foo"]
};
var result = tv4.validateResult(data, schema);
This validation does not work (if I put an integer instead of a string it passes the validation)
What am I doing wrong here?
Disclaimer: I have never used TV4 before.
I'd guess that the schema should specify the foo property as an object with a string property... Something like:
{
"type": "object",
"properties": {
"foo": {
"properties": {
"test": {
"type": "string"
}
},
"type": "object"
}
},
"required": ["foo"]
}
After a quick look at this forum question I figured out that I am missing the "properties" attribute for the sub tree. Now it will work (when the value is an integer it will fail the validation.
I have SpiderMonkey AST, this is javascript object. I use "Esprima", "Acorn" or other libs to generate AST from javascript file. My AST is specified by Mozilla parser API.
I want to manipulate this AST from javascript - remove/add/edit nodes in it. How to do it safely?
For example i want to replace all boolean Literal with !1 or !0.
Now i use walker from "Acorn" lib:
acornWalk.simple(tree, {
Literal: function(node, scope) {
if(typeof node.value === 'boolean'){
var expressionTrue = {
"type": "UnaryExpression",
"operator": "!",
"prefix": true,
"argument": {
"type": "Literal",
"value": 0,
"raw": "0"
}
},
expressionFalse = {
"type": "UnaryExpression",
"operator": "!",
"prefix": true,
"argument": {
"type": "Literal",
"value": 1,
"raw": "1"
}
},
replace = function(node, newNode){
for(var prop in newNode){
node[prop] = newNode[prop];
}
};
replace(node, node.value ? expressionTrue : expressionFalse);
}
}
});
But it seems to be not good practice because the "start", "end" properties in node is not changed and it can break code generated from that AST. Am i right?