how to create DOM element structure in javascript - javascript

I've got this code and I need to transform ti to DOM structure (more information below)
const data = [
{
"type": "paragraph",
"children": [
{
"type": "text",
"text": "Hey all!"
},
{
"type": "break"
},
{
"type": "break"
},
{
"type": "text",
"text": " It's been a while since we partied "
},
{
"type": "important",
"children": [
{
"type": "text",
"text": "together"
}
]
},
{
"type": "text",
"text": " in a pool full of people!"
}
]
},
{
"type": "heading",
"id": "table-of-contents",
"level" : 2,
"children": [
{
"type": "text",
"text": "Table of contents:"
}
]
},
{
"type": "list",
"bullet": "decimal",
"children": [
{
"type": "listitem",
"children": [
{
"type": "anchor",
"href": "#table-of-contents",
"children": [
{
"type": "text",
"text": "How to start a podcast?"
},
{
"type": "text",
"text": ""
}
]
},
{
"type": "text",
"text": "Where to find your topics?"
}
]
},
{
"type": "listitem",
"children": [
{
"type": "text",
"text": "Where to find your topics?"
}
]
},
{
"type": "listitem",
"children": [
{
"type": "text",
"text": "What equipment do you need?"
}
]
}
]
}
]
What is the best way to do it?
I mean, should I do
const wrapper = document.createElement("div");
data.forEach(element => {
if(element.type === "paragraph") {
const paragraph = document.createElement("p");
element.children.forEach(kiddo => {
if(kiddo.type === "text") {
const textNode = document.createTextNode(kiddo.text);
paragraph.appendChild(textNode);
}
});
}
})
..and so on? I mean do I have to use "createElement/createTextNode" functions or does javascript have some kind of DOMBuilder than I can convert such structure into DOM?

As Teemu says, you can create your own "DOM Builder" by adding methods to an object and recursing.
const body = document.getElementsByTagName("body")[0];
const wrapper = document.createElement("div");
const DOMBuilder = {
"anchor" : e => {
var a = document.createElement("a");
a.href = e.href;
return a;
},
"heading" : e => { return document.createElement("h" + e.level); },
"list" : e => {
return document.createElement((e.bullet == "decimal") ? "ol" : "ul");
},
"listitem" : () => { return document.createElement("li"); },
"paragraph" : () => {return document.createElement("p"); },
"text" : e => {return document.createTextNode(e.text); },
}
function CreateDOMElement(e) {
var ne;
if (ne = DOMBuilder[e.type]?.(e)) {
if (e.id) ne.id = e.id;
e.children?.forEach(c => {
var ce = CreateDOMElement(c); if (ce) ne.appendChild(ce);
});
return ne;
}
}
data.forEach(element => {
var ne = CreateDOMElement(element); if (ne) wrapper.appendChild(ne);
});
body.appendChild(wrapper);

Related

How to group keys from a nested object?

Hya 👋
Suppose we have a dynamic object like so:
[
{
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"parent": {
"type": "page_id",
"page_id": "d4b96daf-47a3-4a04-b200"
},
"type": "child_database",
"child_database": {
"title": "Hero"
}
},
{
"object": "page",
"id": "d3022361-96d2-4e15-999e",
"parent": {
"type": "database_id",
"database_id": "089cd0d8-ccbf-4e9e-97a6"
},
},
{
"object": "block",
"id": "a0cba166-1787-4e30-8cc3",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_1",
"heading_1": {
"rich_text": [
{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}
],
}
},
{
"object": "block",
"id": "dbfdd892-8c04-4de3-bf0e",
"parent": {
"type": "page_id",
"page_id": "d3022361-96d2-4e15-999e"
},
"type": "heading_2",
"heading_2": {
"rich_text": [
{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}
],
}
}
]
I would like to reconstruct this object by grouping them based on parent-child like relationship. Since every object has "parent" prop.
The desired result should be like so, where the elements that share the same parent are grouped under child array.
{
"d4b96daf-47a3-4a04-b200": {
"object": "block",
"id": "089cd0d8-ccbf-4e9e-97a6",
"type": "child_database",
"child": [{
"d3022361-96d2-4e15-999e": {
"object": "page",
"child": [{
"a0cba166-1787-4e30-8cc3": {
"object": "block",
"type": "heading_1",
"heading_1": {
"rich_text": [{
"type": "text",
"text": {
"content": "Introduction",
"link": null
},
"plain_text": "Introduction",
"href": null
}]
}
}
},
{
"dbfdd892-8c04-4de3-bf0e": {
"object": "block",
"type": "heading_1",
"heading_2": {
"rich_text": [{
"type": "text",
"text": {
"content": "This is introduction section",
"link": null
},
"plain_text": "This is introduction section",
"href": null
}]
}
}
}
]
}
}]
}
}
Current workaround
/**
* Generator that traverses through nested object
*/
function* traverse(xs: any[] = []): any {
for (let x of xs) {
yield x
yield* traverse(x.child || [])
}
}
/**
* If the property exists in the nested object, then return node
*/
const deepFind = (block: any, pred: any) => (obj: any) => {
for (let node of traverse([obj])) {
if (pred(node)) {
return node
}
}
}
const findById = (block: any) => (obj: any) => deepFind(block, (o: any) => o[block.id])(obj)
export default async function group(pages: Page[]) {
// stuck here 🙏
}
You can do this linearly: create a Map id=>object, iterate the list, if the parent is already on the map, add your object to the parent.child, otherwise create a placeholder object with the parent's id.
let m = new Map()
for (let obj of data) {
let dummy = {id: 'dummy', child: []}
let oid = obj.id
m.set(oid, {...dummy, ...obj, ...m.get(oid)})
let pid = obj.parent.page_id // or whatever depending on type
m.set(pid, m.get(pid) ?? dummy)
m.get(pid).child.push(obj)
}
In the end, the m.values() will contain a flat list of objects with child arrays properly populated.

Rename keys in reduce function

I am essentially receiving data from another API that takes the following structure
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
],
};
What I was doing what creating a new array which essentially gets each unique type and then does a total count and an individual count of each unique name.
I also have another data set I combine with this but omitted for this question.
The working code I have is
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
],
};
const dataReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
'email.success': 0,
'email.failed': 0,
},
];
}
acc[o.type][0].count = (acc[o.type][0].count || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, {});
console.log(dataReduced);
What I can't figure out however, because it is matching on email.success is how to rename these in my final output. I essentially want to remove the email. part.
So instead, the console.log should be
{
Type 1: [{
count: 2,
failed: 1,
success: 1
}],
Type 2: [{
count: 1,
failed: 0,
success: 1
}],
Type 3: [{
count: 1,
failed: 0,
success: 1
}]
}
How would I achieve this?
Thanks
You can do something like this
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
],
};
const dataReduced = ob.dataOne.reduce((acc, o) => {
const name = o.name.replace('email.', '')
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
'success': 0,
'failed': 0,
},
];
}
acc[o.type][0].count = (acc[o.type][0].count || 0) + 1;
acc[o.type][0][name] = acc[o.type][0][name] + 1;
return acc;
}, {});
console.log(dataReduced);
I've no clue why the console output is actually in your Browser console and not in this JS Constainer, but here, no reduce, but I don't even see the reason why a reduce would be better here:
var arr = [
{
"type": "Type 1",
"name": "email.failed"
},
{
"type": "Type 1",
"name": "email.success"
},
{
"type": "Type 2",
"name": "email.success"
},
{
"type": "Type 3",
"name": "email.success"
},
];
var result = [];
for (var o of arr) {
if (!result.hasOwnProperty(o.type)) {
var newObj = {
count: 1,
failed: 0,
success: 0,
};
if (o.name.indexOf('failed') !== -1) {
newObj.failed++;
}
if (o.name.indexOf('success') !== -1) {
newObj.success++;
}
result[o.type] = [newObj];
} else {
result[o.type][0].count++;
if (o.name.indexOf('failed') !== -1) {
result[o.type][0].failed++;
}
if (o.name.indexOf('success') !== -1) {
result[o.type][0].success++;
}
}
}
console.log(result);
I resolved this using the for in loop:
for(let elem in dataReduced){
dataReduced[elem][0]['success'] = dataReduced[elem][0]['email.success'];
dataReduced[elem][0]['failed'] = dataReduced[elem][0]['email.failed'];
delete dataReduced[elem][0]['email.success'];
delete dataReduced[elem][0]['email.failed'];
}

Joining two data sets and calculating different values

So I have an Object of two arrays that I was trying to combine.
What I am essentially aiming to do is get the unique types from dataOne. I then need the total count of occurrences for each type. Then, I need to know for each type the total count of success and failed.
Then from dataTwo I need to include the number of clicks for each type.
At the moment I have the following which achieves this, although I am sure it can be tidied up.
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, dataOneReduced);
console.log(result);
What I am now trying to do is insert the success rate as a percentage. So I need the output to be like so
{
"Type 1": [
{
"count": 2,
"success": 1,
"failed": 1,
"click": 2,
"successPecentage": 50
}
],
"Type 2": [
{
"count": 1,
"success": 1,
"failed": 0,
"click": 2,
"successPecentage": 100
}
],
"Type 3": [
{
"count": 1,
"success": 1,
"failed": 0,
"click": 1,
"successPecentage": 100,
}
]
}
How would I achieve this?
Thanks
Option 1
You can loop over your object one more time and insert the percentage. It is not the best solution performance-wise, but it's simpler.
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, dataOneReduced);
for (const type of Object.keys(result)) {
const obj = result[type][0];
obj.successPercentage = obj.success / obj.count * 100;
}
console.log(result);
Option 2
You can insert the calculation directly into .reduce(), and it will work fine because only the value will be overwritten with each iteration, and only the last, correct value will be in the output.
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
acc[o.type][0].successPercentage = acc[o.type][0].success / acc[o.type][0].count * 100;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, dataOneReduced);
console.log(result);
The desired objective may be achieved by adding one line:
acc[o.type][0].successPercentage = Math.round(acc[o.type][0].success / acc[o.type][0].count * 100); as shown in below snippet
Code Snippet
const ob = {
"dataOne": [
{
"type": "Type 1",
"name": "failed"
},
{
"type": "Type 1",
"name": "success"
},
{
"type": "Type 2",
"name": "success"
},
{
"type": "Type 3",
"name": "success"
},
],
"dataTwo": [
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 2",
"name": "click",
},
{
"type": "Type 1",
"name": "click",
},
{
"type": "Type 3",
"name": "click",
},
]
};
const dataOneReduced = ob.dataOne.reduce((acc, o) => {
if (!acc[o.type]) {
acc[o.type] = [
{
count: 0,
success: 0,
failed: 0,
click: 0,
},
];
}
acc[o.type][0]["count"] = (acc[o.type][0]["count"] || 0) + 1;
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
return acc;
}, {});
const result = ob.dataTwo.reduce((acc, o) => {
acc[o.type][0][o.name] = acc[o.type][0][o.name] + 1;
acc[o.type][0].successPercentage = Math.round(acc[o.type][0].success / acc[o.type][0].count * 100);
return acc;
}, dataOneReduced);
console.log(result);

How to combine specific items in an array of objects depending on the key: value of each object whilst maintaining the order?

I have an array of objects like below. Each object has permanent key 'type' and depending on the 'type', new keys are added.
So if type: 'text', we have a new key 'text'.
If type: 'notText', we have a new key 'attrs'.
arrayOfObj = [
{
"type": "text",
"text": "="
},
{
"type": "text",
"text": " "
},
{
"type": "text",
"text": "S"
},
{
"type": "text",
"text": "O"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Something",
}
},
{
"type": "text",
"text": "H"
},
{
"type": "text",
"text": "E"
},
{
"type": "text",
"text": "Y"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Other",
}
},
{
"type": "text",
"text": "="
},
{
"type": "text",
"text": "T"
},
{
"type": "text",
"text": "O"
},
]
The objects are in order, so depending on the 'type' of each item AND the order, then I need to combine them like so:
arrayOfObj = [
{
"type": "text",
"text": "= SO"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Something",
}
}
{
"type": "text",
"text": "HEY"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Other",
}
},
{
"type": "text",
"text": "=TO"
},
]
In summary, whenever 'type' = 'text' then combine every single object with type 'text' that are together. If the next object has type 'notText', then leave it alone until the next item with 'type' = 'text'. Then combine those.
I've asked a previous question that was similar to this with the working answer
let arrayOfObj = [{
"type": "text",
"text": "="
},
{
"type": "text",
"text": " "
},
{
"type": "text",
"text": "S"
},
{
"type": "text",
"text": "O"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Something",
}
},
{
"type": "text",
"text": "H"
},
{
"type": "text",
"text": "E"
},
{
"type": "text",
"text": "Y"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Other",
}
},
{
"type": "text",
"text": "="
},
{
"type": "text",
"text": "T"
},
{
"type": "text",
"text": "O"
},
];
let newThing = arrayOfObj.reduce(
(textKey, typeKey) => {
if (typeKey.type === "text") {
textKey[0].text += typeKey.text;
} else {
textKey.push({
type: typeKey.type,
attrs: typeKey.attrs
});
}
return textKey;
}, [{
type: "text",
text: ""
}]
);
console.log(newThing);
However it combines all instances where 'type' = 'text' regardless or order or other objects with 'type' = 'notText'.
Would anyone have any idea of how to accomplish this?
I'd suggest using Array.reduce again, starting with an empty array.
If the last item in the array we build up is of type 'text', append the current item, otherwise just add it to the array:
const arrayOfObj = [ { "type": "text", "text": "=" }, { "type": "text", "text": " " }, { "type": "text", "text": "S" }, { "type": "text", "text": "O" }, { "type": "notText", "attrs": { "id": 20, "data": "Something", } }, { "type": "text", "text": "H" }, { "type": "text", "text": "E" }, { "type": "text", "text": "Y" }, { "type": "notText", "attrs": { "id": 20, "data": "Other", } }, { "type": "text", "text": "=" }, { "type": "text", "text": "T" }, { "type": "text", "text": "O" }, ]
const result = arrayOfObj.reduce((acc, curr) => {
// If the last item was of type 'text' _and_ the current item is too.. append the text
if (acc[acc.length-1]?.type === 'text' && curr.type === 'text') {
acc[acc.length-1].text += curr.text;
} else {
// Either current or last item in array was not type 'text'
acc.push(curr)
}
return acc;
}, []);
console.log('Result:', result);
This is how you can do that:
arrayOfObj = [
{
"type": "text",
"text": "="
},
{
"type": "text",
"text": " "
},
{
"type": "text",
"text": "S"
},
{
"type": "text",
"text": "O"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Something",
}
},
{
"type": "text",
"text": "H"
},
{
"type": "text",
"text": "E"
},
{
"type": "text",
"text": "Y"
},
{
"type": "notText",
"attrs": {
"id": 20,
"data": "Other",
}
},
{
"type": "text",
"text": "="
},
{
"type": "text",
"text": "T"
},
{
"type": "text",
"text": "O"
},
]
let output = [];
arrayOfObj.forEach(x => {
if (
x.type == 'notText' ||
output.length == 0 ||
output[output.length - 1].type == 'notText'
) {
output.push(x)
} else {
output[output.length - 1].text += x.text
}
})
console.log(output);
You can use reduce here
const arrayOfObj = [
{ type: "text", text: "=" },
{ type: "text", text: " " },
{ type: "text", text: "S" },
{ type: "text", text: "O" },
{
type: "notText",
attrs: { id: 20, data: "Something" },
},
{ type: "text", text: "H" },
{ type: "text", text: "E" },
{ type: "text", text: "Y" },
{
type: "notText",
attrs: { id: 20, data: "Other" },
},
{ type: "text", text: "=" },
{ type: "text", text: "T" },
{ type: "text", text: "O" },
];
const result = arrayOfObj.reduce((acc, curr) => {
const { type, ...props } = curr;
const last = acc[acc.length - 1];
if (!last || last.type !== "text" || last.type !== type) acc.push(curr);
else last.text = last.text + props.text;
return acc;
}, []);
console.log(result);

I have a json object that I obtained from drafter and I only want the schema

I am using node to call drafter in order to generate the json schema for an application. My goal is to get rid of all the extra output that is spit out by drafter. I end up with a huge thing of json but I only need a small portion of it.
This is what is output:
{
"element": "parseResult",
"content": [
{
"element": "category",
"meta": {
"classes": [
"api"
],
"title": "Test"
},
"attributes": {
"meta": [
{
"element": "member",
"meta": {
"classes": [
"user"
]
},
"content": {
"key": {
"element": "string",
"content": "FORMAT"
},
"value": {
"element": "string",
"content": "1A"
}
}
}
]
},
"content": [
{
"element": "category",
"meta": {
"classes": [
"resourceGroup"
],
"title": "Questions"
},
"content": [
{
"element": "resource",
"meta": {
"title": "Questions"
},
"attributes": {
"href": "/questions"
},
"content": [
{
"element": "transition",
"meta": {
"title": "List All Questions"
},
"content": [
{
"element": "httpTransaction",
"content": [
{
"element": "httpRequest",
"attributes": {
"method": "GET"
},
"content": []
},
{
"element": "httpResponse",
"attributes": {
"statusCode": "200",
"headers": {
"element": "httpHeaders",
"content": [
{
"element": "member",
"content": {
"key": {
"element": "string",
"content": "Content-Type"
},
"value": {
"element": "string",
"content": "application/json"
}
}
}
]
}
},
"content": [
{
"element": "dataStructure",
"content": [
{
"element": "Question List"
}
]
},
{
"element": "asset",
"meta": {
"classes": [
"messageBody"
]
},
"attributes": {
"contentType": "application/json"
},
"content": "[\n {\n \"question\": \"Favourite programming language?\",\n \"published_at\": \"2014-11-11T08:40:51.620Z\",\n \"url\": \"/questions/1\",\n \"choices\": [\n {\n \"choice\": \"Javascript\",\n \"url\": \"/questions/1/choices/1\",\n \"votes\": 2048\n }\n ]\n }\n]"
},
{
"element": "asset",
"meta": {
"classes": [
"messageBodySchema"
]
},
"attributes": {
"contentType": "application/schema+json"
},
"content": "{\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"type\": \"array\"\n}"
}
]
}
]
}
]
}
]
},
{
"element": "resource",
"meta": {
"title": "Question"
},
"attributes": {
"href": "/questions/{id}",
"hrefVariables": {
"element": "hrefVariables",
"content": [
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "id"
},
"value": {
"element": "number",
"content": 1234
}
}
}
]
}
},
"content": [
{
"element": "transition",
"meta": {
"title": "Retrieve Question"
},
"content": [
{
"element": "httpTransaction",
"content": [
{
"element": "httpRequest",
"attributes": {
"method": "GET"
},
"content": []
},
{
"element": "httpResponse",
"attributes": {
"statusCode": "200",
"headers": {
"element": "httpHeaders",
"content": [
{
"element": "member",
"content": {
"key": {
"element": "string",
"content": "Content-Type"
},
"value": {
"element": "string",
"content": "application/json"
}
}
}
]
}
},
"content": [
{
"element": "dataStructure",
"content": [
{
"element": "Question"
}
]
},
{
"element": "asset",
"meta": {
"classes": [
"messageBody"
]
},
"attributes": {
"contentType": "application/json"
},
"content": "{\n \"question\": \"Favourite programming language?\",\n \"published_at\": \"2014-11-11T08:40:51.620Z\",\n \"url\": \"/questions/1\",\n \"choices\": [\n {\n \"choice\": \"Javascript\",\n \"url\": \"/questions/1/choices/1\",\n \"votes\": 2048\n }\n ]\n}"
},
{
"element": "asset",
"meta": {
"classes": [
"messageBodySchema"
]
},
"attributes": {
"contentType": "application/schema+json"
},
"content": "{\n \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"question\": {\n \"type\": \"string\"\n },\n \"published_at\": {\n \"type\": \"string\"\n },\n \"url\": {\n \"type\": \"string\"\n },\n \"choices\": {\n \"type\": \"array\"\n }\n },\n \"required\": [\n \"question\",\n \"published_at\",\n \"url\",\n \"choices\"\n ]\n}"
}
]
}
]
}
]
}
]
}
]
},
{
"element": "category",
"meta": {
"classes": [
"dataStructures"
]
},
"content": [
{
"element": "dataStructure",
"content": [
{
"element": "object",
"meta": {
"id": "Question"
},
"content": [
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "question"
},
"value": {
"element": "string",
"content": "Favourite programming language?"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "published_at"
},
"value": {
"element": "string",
"content": "2014-11-11T08:40:51.620Z"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "url"
},
"value": {
"element": "string",
"content": "/questions/1"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "choices"
},
"value": {
"element": "array",
"content": [
{
"element": "Choice"
}
]
}
}
}
]
}
]
},
{
"element": "dataStructure",
"content": [
{
"element": "object",
"meta": {
"id": "Choice"
},
"content": [
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "choice"
},
"value": {
"element": "string",
"content": "Javascript"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "url"
},
"value": {
"element": "string",
"content": "/questions/1/choices/1"
}
}
},
{
"element": "member",
"attributes": {
"typeAttributes": [
"required"
]
},
"content": {
"key": {
"element": "string",
"content": "votes"
},
"value": {
"element": "number",
"content": 2048
}
}
}
]
}
]
},
{
"element": "dataStructure",
"content": [
{
"element": "array",
"meta": {
"id": "Question List"
},
"content": [
{
"element": "Question"
}
]
}
]
}
]
}
]
}
]
}
The bit below is what I need.
content = {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"question": {
"type": "string"
},
"published_at": {
"type": "string"
},
"url": {
"type": "string"
},
"choices": {
"type": "array"
}
},
"required": [
"question",
"published_at",
"url",
"choices"
]
}
This is the code I have as of right now and it is not working the way I am envisioning. If you need any more information to help me please ask.
App.js
var fs = require('fs');
var edit = require('string-editor');
var lodash = require('lodash');
var _ = require('underscore');
const util = require('util');
const exec = require('child_process').exec;
exec('drafter -f json test.apib' , function(error, stdout, stderr) {
const json = JSON.parse(stdout);
//console.log(json)
var res
function loopThrough(obj){
for(var key in obj){
if(!obj.hasOwnProperty(key)) continue;
if(typeof obj[key] !== 'object'){
//if (cond) var x = {'$schema': };
//if (_.hasIn(obj, '$schema')) {
res = res + "\n" + (key+" = "+obj[key]);
//}
} else {
loopThrough(obj[key]);
}
}
}
loopThrough(json);
//parse = JSON.parse(test);
//string = JSON.stringify(test, null, ' ');
//string = string.replace(/\\n/g, '');
fs.writeFile('test.json', res, function(err) {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
if (error !== null) {
console.log('exec error: ' + error);
}
});
This is the ouput I am down to.
undefined
element = parseResult
element = category
0 = api
title = Test
element = member
0 = user
element = string
content = FORMAT
element = string
content = 1A
element = category
0 = resourceGroup
title = Questions
element = resource
title = Questions
href = /questions
element = transition
title = List All Questions
element = httpTransaction
element = httpRequest
method = GET
element = httpResponse
statusCode = 200
element = httpHeaders
element = member
element = string
content = Content-Type
element = string
content = application/json
element = dataStructure
element = Question List
element = asset
0 = messageBody
contentType = application/json
content = [
{
"question": "Favourite programming language?",
"published_at": "2014-11-11T08:40:51.620Z",
"url": "/questions/1",
"choices": [
{
"choice": "Javascript",
"url": "/questions/1/choices/1",
"votes": 2048
}
]
}
]
element = asset
0 = messageBodySchema
contentType = application/schema+json
content = {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "array"
}
element = resource
title = Question
href = /questions/{id}
element = hrefVariables
element = member
0 = required
element = string
content = id
element = number
content = 1234
element = transition
title = Retrieve Question
element = httpTransaction
element = httpRequest
method = GET
element = httpResponse
statusCode = 200
element = httpHeaders
element = member
element = string
content = Content-Type
element = string
content = application/json
element = dataStructure
element = Question
element = asset
0 = messageBody
contentType = application/json
content = {
"question": "Favourite programming language?",
"published_at": "2014-11-11T08:40:51.620Z",
"url": "/questions/1",
"choices": [
{
"choice": "Javascript",
"url": "/questions/1/choices/1",
"votes": 2048
}
]
}
element = asset
0 = messageBodySchema
contentType = application/schema+json
content = {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"question": {
"type": "string"
},
"published_at": {
"type": "string"
},
"url": {
"type": "string"
},
"choices": {
"type": "array"
}
},
"required": [
"question",
"published_at",
"url",
"choices"
]
}
element = category
0 = dataStructures
element = dataStructure
element = object
id = Question
element = member
0 = required
element = string
content = question
element = string
content = Favourite programming language?
element = member
0 = required
element = string
content = published_at
element = string
content = 2014-11-11T08:40:51.620Z
element = member
0 = required
element = string
content = url
element = string
content = /questions/1
element = member
0 = required
element = string
content = choices
element = array
element = Choice
element = dataStructure
element = object
id = Choice
element = member
0 = required
element = string
content = choice
element = string
content = Javascript
element = member
0 = required
element = string
content = url
element = string
content = /questions/1/choices/1
element = member
0 = required
element = string
content = votes
element = number
content = 2048
element = dataStructure
element = array
id = Question List
element = Question
You could try running it through an online JSON schema generation tool like this: http://jsonschema.net/#/
You will also want to consider reading up on the proposed JSON Schema specification here: http://json-schema.org/documentation.html
UPDATED: If you would like to generate the schema at runtime via Node, you could leverage a module like json-schema-generator.
UPDATED AGAIN: I am not sure I follow you but, from looking at your data, you should be able to use the following to grab all content with the contentType of application/schema+json like so:
var _ = require('lodash');
var schemas = [];
function isSchemaType(contentItem) {
return _.get(contentItem, 'attributes.contentType') == 'application/schema+json';
}
function parseContent(content) {
if (_.isObject(content) && _.isArray(content.content)) {
_.forEach(content.content, parseContent);
} else if (isSchemaType(content)) {
schemas.push(JSON.parse(content.content));
}
}
parseContent(jsonData);
console.log(schemas);
Here is a working jsfiddle: https://jsfiddle.net/k7dcd6s2/

Categories

Resources