Im struggling to parse Json for a desired need.
I am extracting Data from a GraphQL call, and need to parse the response and then generate an XML file.
Example JSON Resonse :
{
"data": {
"products": {
"results": [{
"masterData": {
"current": {
"name": "Paragon Pen",
"description": "TEST",
"categories": [{
"name": "Writing Instrument"
}]
}
}
}]
}
}
}
I need to parse this and make an xml file like this:
<?xml version="1.0"?><product><name>Paragon Pen</name><description>TEST</description><category>Writing Instrument</category></product>
I cant get a method to parse and then hold onto the parent element.
For example, i want to make the xml tag and then add the 'name' element (Writing Instrument) inside this tag. Any help is appreciated.
My Parse method so far.
:
//extractJSON function. Get the information and write it to the file.
function extractJSON(obj) {
for (const i in obj) {
if (Array.isArray(obj[i]) || typeof obj[i] === 'object') {
extractJSON(obj[i]);
} else {
if(VERBOSE){console.log(i + ': ' + obj[i]);}
//Call next step. Write to file.
eventEmitter.emit('writeToXML',i,obj[i]);
}
}
}
why to parse the entire object?
You should be able to iterate throught the array and emit the events
data.products.results.forEach(result, index => eventEmitter.emit('writeToXML',index,result))
nevertheless the example xml just support one product, is that correct? so each emit creates its own xml file?
You could pass the name as extra argument to your recursive function.
Here is how you could build the XML string with plain JavaScript:
function encodeXML(s) { // Utility to escape reserved characters in XML data
const map = { "<": "<", ">": ">", "&": "&", "'": "&apos", '"': """ };
return String(s).replace(/[<>&'"]/g, m => map[m]);
}
function extractJSON(name, val) {
const content = Array.isArray(val) ? val.map( elem => extractJSON("item", elem) ).join``
: Object(val) === val ? Object.keys(val).map(key => extractJSON(key, val[key]) ).join``
: encodeXML(val);
return `<${name}>${content}</${name}>`;
}
// Example:
var obj = {"data":{"products":{"results":[{"masterData":{"current":{"name":"Paragon Pen","description":"TEST","categories":[{"name":"Writing Instrument"}]}}}]}}};
console.log(extractJSON("data", obj.data));
Note that for arrays, this does not create numerical child tags ( <0>, <1> ), but instead a hard-coded <item> tags, one for each array element.
If you only need the deeply nested part (like in your desired output), then, just pass a reference to that nested object:
function encodeXML(s) { // Utility to escape reserved characters in XML data
const map = { "<": "<", ">": ">", "&": "&", "'": "&apos", '"': """ };
return String(s).replace(/[<>&'"]/g, m => map[m]);
}
function extractJSON(name, val) {
const content = Array.isArray(val) ? val.map( elem => extractJSON("item", elem) ).join``
: Object(val) === val ? Object.keys(val).map(key => extractJSON(key, val[key]) ).join``
: encodeXML(val);
return `<${name}>${content}</${name}>`;
}
// Example:
var obj = {"data":{"products":{"results":[{"masterData":{"current":{"name":"Paragon Pen","description":"TEST","categories":[{"name":"Writing Instrument"}]}}}]}}};
console.log(extractJSON("product", obj.data.products.results[0].masterData.current));
Note that the categories array will be really treated as such, so you still get an <item> level in there.
Related
I am having a below json array and now I need to iterate over the json object to retrieve two values of fields ServicePort And ServiceAddress and form a final output as {"MyIp" : "http://IP:Port"} from my json array object.
var bodyObject = [
{
"ServiceAddress": "10.X.X.125",
"ServiceConnect": {},
"ServicePort": 80
},
{
"ServiceAddress": "10.X.X.126",
"ServiceConnect": {},
"ServicePort": 80
}
];
I have tried as below to iterate
for (var key in bodyObject ) {
if (bodyObject.hasOwnProperty(key)) {
console.log(bodyObject[key].ServiceAddress);
console.log(bodyObject[key].ServicePort);
}
}
How can I form a output final output like {"MyIp" : "http://IP:Port"} from my json array object each hitting giving me a diffrent Ip's from my above JSON list dynamically. Can someone help on this please
I think you're asking how to create a new array with a single object with a MyIp property whose value is the combination of ServiceAddress and ServicePort. map is the idiomatic way to do that, perhaps with some destructuring to pick out the properties from each object and a template literal to build the resulting string:
const result = bodyObject.map(({ServiceAddress, ServicePort}) => {
return {MyIp: `http://${ServiceAddress}:${ServicePort}`};
});
or with a concise-form arrow function:
const result = bodyObject.map(({ServiceAddress, ServicePort}) =>
({MyIp: `http://${ServiceAddress}:${ServicePort}`})
);
(You need the () around the object literal because otherwise it looks like the full function body form of arrow function to the parser.)
Live Example:
const bodyObject = [
{
"ServiceAddress": "10.X.X.125",
"ServiceConnect": {},
"ServicePort": 80
},
{
"ServiceAddress": "10.X.X.126",
"ServiceConnect": {},
"ServicePort": 80
}
];
const result = bodyObject.map(({ServiceAddress, ServicePort}) =>
({MyIp: `http://${ServiceAddress}:${ServicePort}`})
);
console.log(result);
That has a fair number of newish JavaScript features in it, so just for clarity here's a version without destructuring or a template literal:
const result = bodyObject.map(element => {
return {MyIp: "http://" + element.ServiceAddress + ":" + element.ServicePort};
});
Currently, I have a JSON file that looks like this:
"1E10BC5D4EC68EE2916BFD97F23E951C": "Seattle Seahawks",
"E6B87019417436B73B62F7802763A289": "Seaside style. ",
"81EEA9E6400BFEADE161559AF14EE468": " {1}",
"6F02148E4A78B33C1CEB75BC2753CA69": " {EndDate}",
"D89CA2634FFF8FA02D028096BAAE6963": "\"You have received a reward for completing the {Bundle Name} {Number} challenges!",
"Mission": {
"Default Mission Info Description": "Default Mission Description",
"Default Mission Info Name": "Default Mission Name",
"RewardDescription": "You ranked {PositionRank}. For your efforts, you have been awarded:",
"NonParticipationRewardDescription": "Your teammates did a great job! For their efforts, you have been awarded:",
"RewardTitle": "{MissionName} Completed!"
}
It goes on for about 40,000 lines, and I would like to modify all of the strings set by it. Currently, I am using #zuzak/owo to try to accomplish this. My current code looks like this:
const owo = require('#zuzak/owo')
fs = require('fs');
var name = '../jsonfile.json';
var data = fs.readFileSync(name).toString();
fs.writeFileSync('../owo.json', JSON.stringify(owo(data)))
How would I be able to only change the strings, such as "Seaside style. " and not edit any of the string names, such as "81EEA9E6400BFEADE161559AF14EE468" (sorry for any incorrect wording, hopefully you can understand what I am saying.)
In case you need it, here is the main code used in #zuzak/owo:
const addAffixes = (str) => randomItem(prefixes) + str + randomItem(suffixes)
const substitute = (str) => {
const replacements = Object.keys(substitutions)
replacements.forEach((x) => {
str = replaceString(str, x, substitutions[x])
})
return str
}
Parse the JSON, iterate over keys, modify values. If necessary recursively iterate over subobjects too:
function owoifyObject (obj) {
for (const key in obj) {
if (typeof obj[key] === 'string') {
obj[key] = owo(obj[key])
} else if (typeof obj[key] === 'object' && obj[key]) {
owoifyObject(obj[key])
}
}
}
const data = JSON.parse(fs.readFileSync('file.json').toString())
owoifyObject(data)
fs.writeFileSync('file2.json', JSON.stringify(data, null, 4))
Note that the argument 4 in JSON.stringify is purely there for formatting so that the result looks something like your input did, otherwise you'd get all the data in a single line.
Use JSON.parse() to parse the JSON file into an object. Then go through the object calling owo() on each value.
var data = JSON.parse(fs.readFileSync(name).toString());
for (let key in data) {
data[key] = owo(data[key]);
}
fs.writeFileSync("../owo.json", JSON.stringify(data));
I need to remove any slashes from all keys (and sub keys) in a JSON structure in order to convert it to XML, where a slash cannot occur in a tag name.
"langServices": {"en/ENGLISH_ONLY": "English"}
I imagine something along the lines of
var finalData = jsonstr.replace("en/", "en-");
, replacing all slashes with a dash. So it should also work for this: {"can/cancel" : "true"}, where I don't know what string will come before the slash.
var jsonIn = {
"some/other/key/with/slashes": "foo bar baz",
"langServices": {
"en/ENGLISH_ONLY": "English",
"can/cancel": "true"
}
};
function sanitizeKeysRecursively(objIn) {
Object.entries(objIn).forEach(function(kv) {
var sanitizedKey = kv[0].replace(/\//g, "-");
// call the function recursively on any values that are objects
if (typeof kv[1] === 'object') {
sanitizeKeysRecursively(kv[1]);
}
// set the sanitized key and remove the unsanitized one
if (sanitizedKey != kv[0]) {
objIn[kv[0].replace(/\//g, "-")] = kv[1];
delete objIn[kv[0]];
}
});
}
sanitizeKeysRecursively(jsonIn);
console.log(jsonIn);
My JSON contains 'products' that each have multiple values (name, manufacturer etc.)
I have the following code that allows me to pull 'products' from my JSON based on a search result acquired from a query string. It works fine, but only if the search entry is formatted exactly how it is written in the string.
How can I allow case insensitive searches yield the desired result (and ideally, partial searches)?
I believe that regular expressions are the way to go, but I've exhausted my knowledge and can't seem to get anywhere with it.
I tried to put together a fiddle but couldn't get a proper demo working, so instead I've tried to consolidate the code and comment it for clarity. Any help would be greatly appreciated :)
Parse the JSON:
var prodobjects = JSON.parse(prods);
Code to get products based on specific value:
function getData(array, type, val) {
return array.filter(function (el) {
return el[type] === val;
});
}
Code to retrieve query string:
function gup( name ){
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if( results == null ) return "";
else return results[1];}
Section of query string I want to use:
var prodresult = gup( 'search' );
Remove plusses and replace with spaces:
var removeplus = prodresult.replace(/\+/g, ' ');
Compile list of products with their 'Prodname' matching the 'search' query:
var searchname = getData(prodobjects.products, 'Prodname', removeplus);
And as requested here is a sample of the JSON. It's still in development so the null values etc. are currently being worked out (it's received through an api). This is just one of the products, but the actual string contains many in the same format (but within "products"):
var prods = JSON.stringify({"products": [
{
"Prodname": null,
"Oem": "Example OEM",
"Snippet": "Example Snippet",
"Linkto": "www.linkhere.com",
"Imagesource": "image.png",
"Category": "Category",
"Tagline": "Tagline goes here",
"Longdescription": [
{
"Paragraph": "<p>First description of the paragraph</p>"
},
null,
null,
null
],
"Features": null,
"Company": false,
"Subscribed": false,
"Tariffs": [
{
"Tarname": "Tariff one",
"Tarpaysched": "Monthly per User",
"Tarcost": "£1"
},
null,
null,
null,
null,
null
],
"Extratariffs": null
}
]
});
---UPDATE---
I managed to get it working to support partial searches and case insensitivity with the following:
function getData(array, type, val) {
return array.filter(function (el) {
if (el[type]!=null && val!=null) {
var seeker = val.toLowerCase();
var rooted = el[type].toLowerCase();
var boxfresh = rooted.indexOf(seeker);
if (boxfresh!=-1) {
return rooted
}
}
});
}
You can convert two strings to lowercase (or uppercase) to make the comparison case-insensitive.
function getData(array, type, val) {
return array.filter(function (el) {
return el[type].toLowerCase() === val.toLowerCase();
});
}
For better searching, you might want to look into fuzzy comparison, which is "a search against data to determine likely mispellings and approximate string matching".
I'm trying to build a quick and dirty static site generator for myself.
Let's say I have this test.html file:
{title}
{downloadpath}
This is my current.json where I get the values i want to replace:
{
"id": 123,
"album" : [{
"title": "Test EP",
"albumid": 1234,
"path": "test.zip"
}]
}
My replacement function looks like this:
// Iterate through JSON object and replace
function iterate(obj) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object")
iterate(obj[property]);
else
console.log("replace {" + property + "} with " + obj[property] )
htmldata.replace(/\{property\}/g, obj[property]);
}
}
}
iterate(json)
var result = htmldata
console.log(result)
// Write new HTML
fs.writeFile("test-" + json.id + ".html", result, 'utf8', function (err) {
if (err) {
return console.log(err);
}
});
and if I run it it works like this:
replace {id} with 123
replace {title} with Test EP
replace {albumid} with 1234
replace {path} with test.zip
{title}
{path}
and you can see the problem right there. I think it's always replacing the edited file with the input file so I don't see any changes. I can't figure it out and if someone could point me in the right direction I'd be grateful.
Thanks!
Not using braces around your if statements will lead to subtle bugs!
You want:
if (typeof obj[property] == "object") {
iterate(obj[property]);
} else {
console.log("replace {" + property + "} with " + obj[property] )
htmldata.replace(/\{property\}/g, obj[property]);
}
Otherwise the replace will run every time regardless of the condition on the if.
Second thing: your regex tries to match the literal string "{property}". Instead, try this:
htmldata.replace(new RegExp("{" + property + "}", "g"), obj[property]);
Third thing: you're not assigning the result of the replace back to htmldata. So you need to do this:
htmldata = htmldata.replace(new RegExp("{" + property + "}", "g"), obj[property]);