Deep level xml parsing to csv using NodeJS - json

I have a medium sized xml ~ 5mb that needs to be converted to csv.
Obviously wont go for reinventing the wheel,
so a two layer approach -
1> xml to json
2> json to csv
My current code is :
const xml_obj = {}
const htt = require('http-status-code-node');
var fs = require('fs');
var xml2js = require('xml2js');
var converter = require('json-2-csv');
xml_obj["convert"] = (req, res, next) => {
var parser = new xml2js.Parser();
fs.readFile(__dirname + '/directoryexport.xml', function (err, data) {
parser.parseString(data, function (err, result) {
console.log('Done');
var callback = function (err, ycsv) {
if (err) return console.log(err);
///
res.setHeader('Content-Disposition', 'attachment; filename=testing.csv');
res.set('Content-Type', 'text/csv');
res.status(200).send(result);
///
}
var documents = [];
documents.push(result)
converter.json2csv(documents, callback);
})
});
}
module.exports = xml_obj.convert
However the xml being nested gives a multi layered json which the yields a single string instead of a proper delimited csv..
The current output CSV
The Original xml
The XML structure
The Json I get on converting xml
Also as per the documentation of the json to csv converter
if the input json is in a proper structure like :
[
{
Make: 'Nissan',
Model: 'Murano',
Year: '2013',
Specifications: {
Mileage: '7106',
Trim: 'S AWD'
}
},
{
Make: 'BMW',
Model: 'X5',
Year: '2014',
Specifications: {
Mileage: '3287',
Trim: 'M'
}
}
];
This yields a very nicely formatted csv like this : Example Perfect CSV From JSON
Edit 1 :
The format I'm looking for is somewhat like,
It's important to capture all parent organization and organizationalUnit details for each person node.
For example,
organizationalUnit UUID "b3b05b77-a8a7-43ed-ab74-b7d898c60296" should
produce a CSV lines like:
"Mr Shayne Howard","Howard","Shayne","Mr","","Branch Manager","(02) 6121 5492","","Level 1, 12 Mort Street, Canberra, ACT, 2601","Shayne.Howard#employment.gov.au","","b43e0864-1b9a-40f0-8049-c90af5f9141c","","GPO Box 9880 CANBERRA ACT 2601 Australia",1392,"","Department of Employment","","1300 488 064","","","","http://www.employment.gov.au","GPO Box 9880, Canberra ACT 2601","EMPLOYMENT"
"Mr Luke de Jong","De Jong","Luke","Mr","","Branch Manager, General Counsel","(02) 6240 0909",""(02) 6123 5100"","","Luke.deJong#employment.gov.au","","58a503a8-ce8b-41c0-b690-b9f9efd98a89","","GPO Box 9880 CANBERRA ACT 2601",1393,"","Department of Employment","","1300 488 064","","","","http://www.employment.gov.au","GPO Box 9880, Canberra ACT 2601","EMPLOYMENT"
Edit 2 :
Flattening out the json is a good idea, but its not capturing the entire data.
Using the camaro nodejs module with the following template :
persons: ['//person', {
root_organization_name: '../../../../name',
main_organization_name: '../../../name',
main_organization_website: '../../../website',
fullName: 'fullName',
familyName: 'familyName',
firstName: 'firstName',
personalTitle: 'personalTitle',
title: 'title',
person_phone: 'phone',
person_location: 'location',
person_fax: 'fax',
otherRolesDN: 'otherRolesDN',
person_mail: 'mail',
informationPublicationScheme: '../informationPublicationScheme',
publications: '../../publications',
annualReport: '../../annualReport',
mediaReleases: '../../mediaReleases',
organizationUnit_1_name: '../../name',
organizationUnit_1_description: '../../description',
organizationUnit_1_location: '../../location',
organizationUnit_1_phone: '../../phone',
organizationUnit_1_fax: '../../fax',
organizationUnit_1_website: '../../website',
organizationUnit_2_name: '../name',
organizationUnit_2_location: '../location',
organizationUnit_2_phone: '../phone',
organizationUnit_2_fax: '../fax',
organizationUnit_2_website: '../website',
occupantName: './role/occupantName',
roleName: './role/roleName',
occupantUUID: './role/occupantUUID',
role_phone: './role/phone',
role_fax: './role/fax',
role_location: './role/location',
role_mail: './role/ mail'
}]
How could I also get the roles array.
Also current csv gets some rows of data in wrong columns :
Wrong csv after camaro
Any tips on how to make this work with my input.

Because the output of json structure is deep, if you want it to convert to csv properly, you would have to flatten it.
Seems like you only interest in deepest level. here's an example, if you want to add more data, feel free to add to the template
const transform = require('camaro')
const tocsv = require('json2csv')
const fs = require('fs')
const xml = fs.readFileSync('so.xml', 'utf-8')
const template = {
persons: ['//person', {
root_organization_name: '../../../../name',
main_organization_name: '../../../name',
main_organization_website: '../../../website',
fullName: 'fullName',
familyName: 'familyName',
firstName: 'firstName',
personalTitle: 'personalTitle',
title: 'title',
person_phone: 'phone',
person_location: 'location',
person_fax: 'fax',
otherRolesDN: 'otherRolesDN',
person_mail: 'mail',
informationPublicationScheme: '../informationPublicationScheme',
publications: '../../publications',
annualReport: '../../annualReport',
mediaReleases: '../../mediaReleases',
organizationUnit_1_name: '../../name',
organizationUnit_1_description: '../../description',
organizationUnit_1_location: '../../location',
organizationUnit_1_phone: '../../phone',
organizationUnit_1_fax: '../../fax',
organizationUnit_1_website: '../../website',
organizationUnit_2_name: '../name',
organizationUnit_2_location: '../location',
organizationUnit_2_phone: '../phone',
organizationUnit_2_fax: '../fax',
organizationUnit_2_website: '../website',
roles: ['../role', {
occupantName: 'occupantName',
roleName: 'roleName',
occupantUUID: 'occupantUUID',
role_phone: 'phone',
role_fax: 'fax',
role_location: 'location',
role_mail: ' mail'
}]
}]
}
const result = transform(xml, template)
console.log(JSON.stringify(result.roles, null, 4))
Example of output json (use json2csv to convert to csv if you want)

Related

How to read results returned by a query when using mysql in nodejs server? [duplicate]

I'm currently developing a desktop application with Node-webkit. During that process I need to get some data from a local MySQL-database.
The querying works fine, but I can't figure out how to access the results. I store all of them in an array that is then passed to a function. In the console they look like this:
RowDataPacket {user_id: 101, ActionsPerformed: 20}
RowDataPacket {user_id: 102, ActionsPerformed: 110}
RowDataPacket {user_id: 104, ActionsPerformed: 3}
And here is the query structure:
var ret = [];
conn.query(SQLquery, function(err, rows, fields) {
if (err)
alert("...");
else {
for (var i of rows)
ret.push(i);
}
doStuffwithTheResult(ret);
}
How do I retrieve this in the doStuffwithTheResult function? The values are more important, but if I could get the keys as well that would be great.
Turns out they are normal objects and you can access them through user_id.
RowDataPacket is actually the name of the constructor function that creates an object, it would look like this new RowDataPacket(user_id, ...). You can check by accessing its name [0].constructor.name
If the result is an array, you would have to use [0].user_id.
With Object.prototype approach, JSON.parse(JSON.stringify(rows)) returns object, extract values with Object.values()
let result = Object.values(JSON.parse(JSON.stringify(rows)));
Usage:
result.forEach((v) => console.log(v));
I also met the same problem recently, when I use waterline in express project for complex queries ,use the SQL statement to query.
this is my solution: first transform the return value(RowDataPacket object) into string, and then convert this string into the json object.
The following is code :
//select all user (查询全部用户)
find: function(req, res, next){
console.log("i am in user find list");
var sql="select * from tb_user";
req.models.tb_user.query(sql,function(err, results) {
console.log('>> results: ', results );
var string=JSON.stringify(results);
console.log('>> string: ', string );
var json = JSON.parse(string);
console.log('>> json: ', json);
console.log('>> user.name: ', json[0].name);
req.list = json;
next();
});
}
The following is console:
>> results: [ RowDataPacket {
user_id: '2fc48bd0-a62c-11e5-9a32-a31e4e4cd6a5',
name: 'wuwanyu',
psw: '123',
school: 'Northeastern university',
major: 'Communication engineering',
points: '10',
datems: '1450514441486',
createdAt: Sat Dec 19 2015 16:42:31 GMT+0800 (中国标准时间),
updatedAt: Sat Dec 19 2015 16:42:31 GMT+0800 (中国标准时间),
ID: 3,
phone: 2147483647 } ]
>> string: [{"user_id":"2fc48bd0-a62c-11e5-9a32-a31e4e4cd6a5","name":"wuwanyu","psw":"123","school":"Northeastern university","major":"Communication engineering","points":"10","datems":"1450514
441486","createdAt":"2015-12-19T08:42:31.000Z","updatedAt":"2015-12-19T08:42:31.000Z","ID":3,"phone":2147483647}]
>> json: [ { user_id: '2fc48bd0-a62c-11e5-9a32-a31e4e4cd6a5',
name: 'wuwanyu',
psw: '123',
school: 'Northeastern university',
major: 'Communication engineering',
points: '10',
datems: '1450514441486',
createdAt: '2015-12-19T08:42:31.000Z',
updatedAt: '2015-12-19T08:42:31.000Z',
ID: 3,
phone: 2147483647 } ]
>> user.name: wuwanyu
Hi try this 100% works:
results=JSON.parse(JSON.stringify(results))
doStuffwithTheResult(results);
You can copy all enumerable own properties of an object to a new one by Object.assign(target, ...sources):
trivial_object = Object.assign({}, non_trivial_object);
so in your scenario, it should be enough to change
ret.push(i);
to
ret.push(Object.assign({}, i));
you try the code which gives JSON without rowdatapacket:
var ret = [];
conn.query(SQLquery, function(err, rows, fields) {
if (err)
alert("...");
else {
ret = JSON.stringify(rows);
}
doStuffwithTheResult(ret);
}
going off of jan's answer of shallow-copying the object, another clean implementation using map function,
High level of what this solution does: iterate through all the rows and copy the rows as valid js objects.
// function will be used on every row returned by the query
const objectifyRawPacket = row => ({...row});
// iterate over all items and convert the raw packet row -> js object
const convertedResponse = results.map(objectifyRawPacket);
We leveraged the array map function: it will go over every item in the array, use the item as input to the function, and insert the output of the function into the array you're assigning.
more specifically on the objectifyRawPacket function: each time it's called its seeing the "{ RawDataPacket }" from the source array. These objects act a lot like normal objects - the "..." (spread) operator copies items from the array after the periods - essentially copying the items into the object it's being called in.
The parens around the spread operator on the function are necessary to implicitly return an object from an arrow function.
Solution
Just do: JSON.stringify(results)
I found an easy way
Object.prototype.parseSqlResult = function () {
return JSON.parse(JSON.stringify(this[0]))
}
At db layer do the parsing as
let users= await util.knex.raw('select * from user')
return users.parseSqlResult()
This will return elements as normal JSON array.
If anybody needs to retrive specific RowDataPacket object from multiple queries, here it is.
Before you start
Important: Ensure you enable multipleStatements in your mysql connection like so:
// Connection to MySQL
var db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '123',
database: 'TEST',
multipleStatements: true
});
Multiple Queries
Let's say we have multiple queries running:
// All Queries are here
const lastCheckedQuery = `
-- Query 1
SELECT * FROM table1
;
-- Query 2
SELECT * FROM table2;
`
;
// Run the query
db.query(lastCheckedQuery, (error, result) => {
if(error) {
// Show error
return res.status(500).send("Unexpected database error");
}
If we console.log(result) you'll get such output:
[
[
RowDataPacket {
id: 1,
ColumnFromTable1: 'a',
}
],
[
RowDataPacket {
id: 1,
ColumnFromTable2: 'b',
}
]
]
Both results show for both tables.
Here is where basic Javascript array's come in place https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
To get data from table1 and column named ColumnFromTable1 we do
result[0][0].ColumnFromTable1 // Notice the double [0]
which gives us result of a.
db.query('select * from login',(err, results, fields)=>{
if(err){
console.log('error in fetching data')
}
var string=JSON.stringify(results);
console.log(string);
var json = JSON.parse(string);
// to get one value here is the option
console.log(json[0].name);
})
conn.query(sql, (err,res,fields) => {
let rawData = res;
let dataNormalized = {...rawData[0]};
})
//Object Destructuring
This worked for me hope it helps you.
I think it is simplest way to copy object.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Simpler way:
.then( resp=> {
let resultFromDb= Object.values(resp)[0]
console.log(resultFromDb)
}
In my example I received an object in response.
When I use Object.values I have the value of the property as a response, however it comes inside an array, using [0] access the first index of this array, now i have the value to use it where I need it.
I had this problem when trying to consume a value returned from a stored procedure.
console.log(result[0]);
would output "[ RowDataPacket { datetime: '2019-11-15 16:37:05' } ]".
I found that
console.log(results[0][0].datetime);
Gave me the value I wanted.
I had a similar problem and the solution was as follows:
const results = pool.query('sql sentence',[params]);
console.log((results[0])[0].name);
How to ACCESS what you get back from the database, this works for me:
async function getPageId(pageSlug){
let sql_update = 'SELECT id FROM pages WHERE pageSlug = ?';
let arrValues = [
pageSlug
];
let result = await mydb.query(sql_update, arrValues);
let r = JSON.parse(JSON.stringify(result));
if(r?.length){
return r[0].id;
}else{
return false;
}
}
I really don't see what is the big deal with this I mean look if a run my sp which is CALL ps_get_roles();.
Yes I get back an ugly ass response from DB and stuff. Which is this one:
[
[
RowDataPacket {
id: 1,
role: 'Admin',
created_at: '2019-12-19 16:03:46'
},
RowDataPacket {
id: 2,
role: 'Recruiter',
created_at: '2019-12-19 16:03:46'
},
RowDataPacket {
id: 3,
role: 'Regular',
created_at: '2019-12-19 16:03:46'
}
],
OkPacket {
fieldCount: 0,
affectedRows: 0,
insertId: 0,
serverStatus: 35,
warningCount: 0,
message: '',
protocol41: true,
changedRows: 0
}
]
it is an array that kind of look like this:
rows[0] = [
RowDataPacket {/* them table rows*/ },
RowDataPacket { },
RowDataPacket { }
];
rows[1] = OkPacket {
/* them props */
}
but if I do an http response to index [0] of rows at the client I get:
[
{"id":1,"role":"Admin","created_at":"2019-12-19 16:03:46"},
{"id":2,"role":"Recruiter","created_at":"2019-12-19 16:03:46"},
{"id":3,"role":"Regular","created_at":"2019-12-19 16:03:46"}
]
and I didnt have to do none of yow things
rows[0].map(row => {
return console.log("row: ", {...row});
});
the output gets some like this:
row: { id: 1, role: 'Admin', created_at: '2019-12-19 16:03:46' }
row: { id: 2, role: 'Recruiter', created_at: '2019-12-19 16:03:46' }
row: { id: 3, role: 'Regular', created_at: '2019-12-19 16:03:46' }
So you all is tripping for no reason. Or it also could be the fact that I'm running store procedures instead of regular querys, the response from query and sp is not the same.

How to get data from database in array format using node js and MySql

I am using node.js as server language and Mysql as database so I am running query and getting data from database but is is showing in format like this
[ BinaryRow { name: 'Dheeraj', amount: '77.0000' },
BinaryRow { name: 'Raju', amount: '255.0000' } ]
What I want is
['Dheeraj', 77.0000],
['Raju', 66255.000030],
This what I am doing in my backend (node.js):
My model:
static getChartData(phoneNo, userType) {
let sql = 'select businessname as name,sum(billamt) amount from cashbackdispdets where consphoneno =' + phoneNo + ' group by businessid order by tstime desc limit 10'
return db.execute(sql, [phoneNo]);
My controller:
exports.getColumnChart = function(req, res) {
const phoneNo = req.body.userId
const userType = req.body.userType
console.log(phoneNo)
dashboardModule.getChartData(phoneNo, userType)
.then(([rows]) => {
if (rows.length > 0) {
console.log(rows)
return res.json(rows)
} else {
console.log("error")
return res.status(404).json({ error: 'Phone No. already taken' })
}
})
.catch((error) => {
console.log(error)
return res.status(404).json({ error: 'Something went wrong !!' })
})
}
I am sending this data to Ui and when I am receiving it on UI it is in the form of object inside array which is not the required data type I want
axios().post('/api/v1/Dashboard/DashboardColumnChart',this.form)
.then(res=>{
console.log(res.data)
debugger
this.chartData= res.data
})
The above code consoles on browser like
I am not getting any idea how o do it should I do it with backend or with front end and how
Nodejs will send you a JSON response if you want to change it. It is better to change or maniuplate it in a Front end framework. But if you want to change it in backend as you have asked Make sure that the rows is in the format that you want to recive.
let data = [
{ "name": "Dheeraj", "amount": "77.0000" },
{ "name": "Raju", "amount": "255.0000" }
]
// empty array to store the data
let testData = [];
data.forEach(element => {
testData.push(element.name)
});
You can format it using array.map and Object.values. map functions loops over each element and returns a modified element according to the callback provided. Object.values simply returns all the values of an object in an array.
const data = [ { "name": "Dheeraj", "amount": "77.0000" }, { "name": "Raju", "amount": "255.0000" } ];
const formattedData = data.map(obj => Object.values(obj));
console.log("Initial Data: ", data);
console.log("Formatted Data: ", formattedData);
// Map function example
const a = [1,2,3]
const mappedA = a.map(e => e * 2)
console.log(a, " mapped to: ", mappedA);
// Object.values example
const b = { firstName: 'John', lastName: 'Doe', number: '120120' }
console.log(Object.values(b));

Destructuring array and accessing the first element

I made and API call using fetch to get JSON data. That data is then passed to my function displayCartTotal, which accepts a parameter that uses de-structuring to obtain results.
In displayCartTotal, I want to de-structure the first item into the results array, into a data variable. Next, use object de-structuring to obtain the itemsInCart and buyerCountry properties of the data.
I have tried de-structuring the array, but is not working, also when i do typeof() on the data I receive, I get "object".
Here is format of the JSON data
{
results: [{
itemsInCart: [{
name: "Jolof Rice",
price: 80,
qty: 2
}, {
name: "Jolof Rice",
price: 80,
qty: 2
}],
buyerCountry: "Uganda"
}],
info: {
seed: "85e0e8ca0e095f74",
results: "1",
page: "1",
version: "0.1",
time: {
instruct: 11,
generate: 5
}
}
}
Code:
const displayCartTotal = ({results}) => {
const [data] = results;
const [itemsInCart,buyerCountry] = data;
return results;
};
const fetchBill = () => {
const api = 'https://randomapi.com/api/006b08a801d82d0c9824dcfdfdfa3b3c';
fetch(api)
.then(response => response.json())
.then(data => displayCartTotal(data))
.catch(error => console.error(error));
};
I expect to de-structure the first item in the results array into a data variable. And also to use object de-structuring to obtain the itemsInCart and buyerCountry properties of data.
Have you tried placing the nth position of the object
const displayCartTotal= ({results})=>{
const {0: data} = results;
const {itemsInCart, buyerCountry} = data;
}

Typescript convert an array to JSON

I have a complicated data structure that I need to convert to JSON. The problem is that my field names and values are in an array.
For instance, I have the following (simplified from my code base):
let SampleData = [
{ Field: 'Key', Value: '7'},
{ Field: 'City', Value: 'Some City'},
{ Field: 'Description', Value: 'Some Description'}
];
Basically my data is an array where the first element is the database column name, and the second element is the data in the column. I am trying to get a JSON object that is:
{ Key: 7, City: 'Some City', Description: 'Some Description' }
My real code has the fields and data is structures within the object, so I cannot simply use an Object.create() or Object.assign() as far as I can get working.
I have tried looping through to build a simple string and then use the JSON.parse to break it apart, but this seems like a lot of overhead for something I would have thought would be simpler.
As you asked, here's how to do it:
Mapping the array to an object
Converting the object to JSON
let array = [{
Field: 'Key',
Value: '7'
},
{
Field: 'City',
Value: 'Some City'
},
{
Field: 'Description',
Value: 'Some Description'
}
];
// #1 Mapping the array to an object...
let obj = {};
array.forEach(item => obj[item.Field] = item.Value);
// #2 Converting the object to JSON...
let json = JSON.stringify(obj);
console.log(json);
Bonus (ES6 + reduce):
const obj = array.reduce((acc, { Field, Value }) => ({ ...acc, [Field]: Value }), {});
you can try the below approach . I have used spread operator(ES6) and Object.assign to create the object ,then converted it into json string.
let SampleData = [
{ Field: 'Key', Value: '7'},
{ Field: 'City', Value: 'Some City'},
{ Field: 'Description', Value: 'Some Description'}
];
let obj = Object.assign(...SampleData.map( x => Object.values(x)).map(y => ({[y[0]]: y[1]})));
console.log(obj);
//{ Key: "7", City: "Some City", Description: "Some Description" }
console.log(JSON.stringify(obj));
I had a similar requirement and here is how I achieved it.
var ranges: segmentRange[] = new Array(2);
ranges[0] = { minimumPercentage: 50, maximumPercentage: 60 };
ranges[1] = { minimumPercentage: 30, maximumPercentage: 40 };
const segmentRanges = { segmentRanges: ranges };
return JSON.stringify(segmentRanges);
Output:
{"segmentRanges":[{"minimumPercentage":50,"maximumPercentage":60},{"minimumPercentage":30,"maximumPercentage":40}]}
HTH,

MongooseError: Cast to ObjectID failed for value on reading json for db seed

I have a model scheme with a reference field like this:
const UserSchema = new mongoose.Schema({
// ...
uf:{
type: mongoose.Schema.Types.ObjectId, ref: 'UF', index: true
},
});
And my test db seed code is consuming data from json files, like this:
[
{ "_id": 91283,
"name":"Test user",
"uf": 124411923,
"version": 2
}
]
During in the seed process, after the model save method, I’m getting this error:
ValidationError: User validation failed: uf: Cast to ObjectID failed for value "124411923" at path "uf"
errors:
{ uf:
{ MongooseError: Cast to ObjectID failed for value "124411923" at path "uf"
This is code responsible for loading the jsons and saving to the database, I've sorted the seed list so that UF is inserted first:
function seed() {
console.log('Starting db seed...');
return Promise.each(initialData, (data) => {
// path to mongo model js file
let Model = require(data.model);
removeModel(Model)
.then(() => {
console.log('[' + data.name + '] model removed. ');
return saveModel(data, Model);
}).then(() => {
console.log('[' + data.name + '] model saved');
}).catch( (err) => {
console.error('Error seeding db', err);
});
});
}
/**
* Saves model to the database
* #param {*} data
* #param {*} Model
*/
function saveModel(data, Model) {
// path to json data file
let seedList = require(data.seed);
return Promise.map(seedList, function(seed) {
let newItem = new Model(seed);
return newItem.save({});
});
}
Can anyone help?
In your user document, you are not including a valid ObjectId, hence the error when you try to store as type ObjectId.
Refs can only defence the _id field from other collections.
Therefore, in your user document, you will need to include a reference to the _id field from the UF collection:
[
{
"_id": 91283,
"name":"Test user",
"uf": [_id from UF here],
"version": 2
}
]
NOTE: If you have explicitly defined the _id field in your UF schema as type Number, then you can reference that from your user schema by matching types:
const UserSchema = new mongoose.Schema({
// ...
uf: {
type: Number, ref: 'UF', index: true
},
});
It seems you don't have a valid ObjectId string. Try using 123456789012 as it's a 12 bytes string.