I am struggling to convert xml to json object and then extracting nodes from the converted object. I am using busyboy to read from the file uploaded on the server. And after that I am using inspect to convert xml to json and then printing the json object. The final output seems as
{ declaration: { attributes: { version: '1.0', encoding: 'utf-8' } },
root:
{ name: 'order',
attributes:
{ orderid: '123456',
xmlns: 'http://www.someRandomNameSpace.com' },
children:
[ { name: 'orderperson',
attributes: {},
children: [],
content: 'str1234' },
{ name: 'shipto',
attributes: {},
children:
[ { name: 'name',
attributes: {},
children: [],
content: 'Adnan Ali' },
I want to read the 'name'='Adnan Ali' from this object how will that be done in nodejs ? I mean how can i reach to the object which has name='name' and content='Adnan Ali'.
The print command is
console.log(inspect(order, {colors: true, depth: Infinity}));
Since you are using NodeJS perhaps giving JSONPath a try would be a good idea. Then you can do something like this:
var jp = require("JSONPath");
var tobj = { "declaration": { "attributes": { "version": '1.0', "encoding": 'utf-8' } },
"root":
{ "name": 'order',
"attributes":
{ "orderid": '123456',
"xmlns": 'http://www.someRandomNameSpace.com' },
"children":
[ { "name": 'orderperson',
"attributes": {},
"children": [],
"content": 'str1234' },
{ "name": 'shipto',
"attributes": {},
"children":
[ { "name": 'name',
"attributes": {},
"children": [],
"content": 'Adnan Ali'
}
]
}
]
}};
var result = jp.eval(tobj, "$..children[?(#.name === 'name' && #.content === 'Adnan Ali')]");
console.log(result);
Example output:
[ { name: 'name',
attributes: {},
children: [],
content: 'Adnan Ali' } ]
(Don't forget to install JSONPath ;-))
Sources:
https://www.npmjs.com/package/JSONPath
http://goessner.net/articles/JsonPath/
You need to search the arrays of objects for the objects you are interested in. There are various ways to do that, including Array.prototype.find (not sure if it is available in all Node.js versions) and lodash _.find.
Using Array.prototype.filter a solution could look like this (not tested):
function findObject(array, key, value) {
var filtered = array.filter(obj => (obj[key] === value));
if (filtered.length !== 1) throw new Error('Found ' + filtered.length + ' objects with `' + key + '`=`' + value + '`, expected to find 1.');
return filtered[0];
}
var shipto = findObject(input.root.children, 'name', 'shipto');
var name = findObject(shipto.children, 'name', 'name').content;
console.log(name);
You should be able to reach the object with content: 'Adnan Ali' with this path data.root.children[1].children[0]:
const data = {
declaration: {
attributes: {
version: '1.0',
encoding: 'utf-8'
}
},
root: {
name: 'order',
attributes: {
orderid: '123456',
xmlns: 'http://www.someRandomNameSpace.com'
},
children: [{
name: 'orderperson',
attributes: {},
children: [],
content: 'str1234'
}, {
name: 'shipto',
attributes: {},
children: [{
name: 'name',
attributes: {},
children: [],
content: 'Adnan Ali'
}]
}]
}
};
console.log(data.root.children[1].children[0])
Explanation:
data is an object that contains a root object. root is an object that contains a children array. The second element in root.children (index 1) is an object that contains another children array that contains the object you're looking for at the first index (0).
Consider using object-scan. It's very powerful once you wrap your head around it.
// const objectScan = require('object-scan');
const find = (input) => objectScan(['**'], {
abort: true,
rtn: 'value',
filterFn: ({ value }) => value.content === 'Adnan Ali' && value.name === 'name'
})(input);
const tobj = { declaration: { attributes: { version: '1.0', encoding: 'utf-8' } }, root: { name: 'order', attributes: { orderid: '123456', xmlns: 'http://www.someRandomNameSpace.com' }, children: [{ name: 'orderperson', attributes: {}, children: [], content: 'str1234' }, { name: 'shipto', attributes: {}, children: [{ name: 'name', attributes: {}, children: [], content: 'Adnan Ali' }] }] } };
console.log(find(tobj));
// => { name: 'name', attributes: {}, children: [], content: 'Adnan Ali' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
Related
We are creating a new version our API (v2) adopting the JSON:API specification (https://jsonapi.org/). I'm not being able to port the ExtJS model associations (belongs_to) to the new pattern.
The ExtJS documentation only shows how to use a nested relation in the same root node (https://docs.sencha.com/extjs/4.2.2/#!/api/Ext.data.association.Association).
v1 data (sample):
{
"data": [
{
"id": 1,
"description": "Software Development",
"area_id": 1,
"area": {
"id": 1,
"code": "01",
"description": "Headquarters"
}
},
],
"meta": {
"success": true,
"count": 1
}
}
v2 data (sample):
{
"data": [
{
"id": "1",
"type": "maint_service_nature",
"attributes": {
"id": 1,
"description": "Software Development",
"area_id": 1
},
"relationships": {
"area": {
"data": {
"id": "1",
"type": "area"
}
}
}
}
],
"included": [
{
"id": "1",
"type": "area",
"attributes": {
"id": 1,
"code": "01",
"description": "Headquarters"
}
}
],
"meta": {
"success": true,
"count": 1
}
}
My model:
Ext.define('Suite.model.MaintServiceNature', {
extend: 'Ext.data.Model',
fields: [
{ desc: "Id", name: 'id', type: 'int', useNull: true },
{ desc: "Area", name: 'area_id', type: 'int', useNull: true },
{ desc: "Description", name: 'description', type: 'string', useNull: true, tableIdentification: true }
],
associations: [
{
type: 'belongsTo',
model: 'Suite.model.Area',
foreignKey: 'area_id',
associationKey: 'area',
instanceName: 'Area',
getterName: 'getArea',
setterName: 'setArea',
reader: {
type: 'json',
root: false
}
}
],
proxy: {
type: 'rest',
url: App.getConf('restBaseUrlV2') + '/maint_service_natures',
reader: {
type: 'json',
root: 'data',
record: 'attributes',
totalProperty: 'meta.count',
successProperty: 'meta.success',
messageProperty: 'meta.errors'
}
}
});
Any ideias on how to setup the association to work with the v2 data?
I'm honestly taking a stab at this one... I haven't used Ext JS 4 in years, and I wouldn't structure my JSON like JSON:API does, but I think the only way you can accomplish this is by rolling your own reader class. Given that you have generic properties for your data structure, this reader should work for all scenarios... although, I'm not too familiar with JSON:API, so I could be totally wrong. Either way, this is what I've come up with.
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.myReader',
root: 'data',
totalProperty: 'meta.count',
successProperty: 'meta.success',
messageProperty: 'meta.errors',
/**
* #override
*/
extractData: function (root) {
var me = this,
ModelClass = me.model,
length = root.length,
records = new Array(length),
dataConverter,
convertedValues, node, record, i;
for (i = 0; i < length; i++) {
node = root[i];
var attrs = node.attributes;
if (node.isModel) {
// If we're given a model instance in the data, just push it on
// without doing any conversion
records[i] = node;
} else {
// Create a record with an empty data object.
// Populate that data object by extracting and converting field values from raw data.
// Must pass the ID to use because we pass no data for the constructor to pluck an ID from
records[i] = record = new ModelClass(undefined, me.getId(attrs), attrs, convertedValues = {});
// If the server did not include an id in the response data, the Model constructor will mark the record as phantom.
// We need to set phantom to false here because records created from a server response using a reader by definition are not phantom records.
record.phantom = false;
// Use generated function to extract all fields at once
me.convertRecordData(convertedValues, attrs, record, me.applyDefaults);
if (me.implicitIncludes && record.associations.length) {
me.readAssociated(record, node);
}
}
}
return records;
}
});
Ext.define('Suite.model.Area', {
extend: 'Ext.data.Model',
fields: [{
name: 'type',
type: 'string'
}]
});
Ext.define('Suite.model.MaintServiceNature', {
extend: 'Ext.data.Model',
fields: [{
desc: "Id",
name: 'id',
type: 'int',
useNull: true
}, {
desc: "Area",
name: 'area_id',
type: 'int',
useNull: true
}, {
desc: "Description",
name: 'description',
type: 'string',
useNull: true,
tableIdentification: true
}],
associations: [{
type: 'belongsTo',
model: 'Suite.model.Area',
associatedName: 'Area',
foreignKey: 'area_id',
associationKey: 'relationships.area.data',
instanceName: 'Area',
getterName: 'getArea',
setterName: 'setArea'
}],
proxy: {
type: 'rest',
url: 'data1.json',
reader: {
type: 'myReader'
}
}
});
Suite.model.MaintServiceNature.load(null, {
callback: function (record) {
console.log(record.getData(true));
}
});
}
});
I have two tables:
const attr = {
name: {
type: DataTypes.STRING,
},
};
const Tags = createModel('Tags', attr, {});
and:
const attr = {
tagId: {
type: DataTypes.INTEGER,
references: { model: 'Tags', key: 'id' },
}
}
const Client = createModel('Client', attr, {})
Client.belongsTo(Tag, { foreignKey: 'tagId', as: 'tags' });
and my query is this:
const clientCount = await Client.findAll({
include: [ { model: Tags, as: 'tags' } ],
attributes: { exclude: 'tagId' }
});
and this is my response:
{
"id": 1,
"createdAt": "2020-01-20T00:00:00.000Z",
"updatedAt": "2020-01-22T00:00:00.000Z",
"tags": {
"id": 1,
"name": "New tag",
"createdAt": "2020-01-20T00:00:00.000Z",
"updatedAt": "2020-01-20T00:00:00.000Z"
}
}
but I want my tags to be an array, so I guest I have to define a one to many association, but everything I tried so far failed.
What I want is tags to be an array, where I can add multiple tag objects:
{
"id": 1,
"createdAt": "2020-01-20T00:00:00.000Z",
"updatedAt": "2020-01-22T00:00:00.000Z",
"tags": [
{
"id": 1,
"name": "New tag",
"createdAt": "2020-01-20T00:00:00.000Z",
"updatedAt": "2020-01-20T00:00:00.000Z"
}
]
}
Method1
We need new model as Client_Tag
const attr = {
clientId: {
type: DataTypes.INTEGER,
},
tagId: {
type: DataTypes.INTEGER,
},
};
const Client_Tag = createModel('Client_Tag', attr, {});
Client.belongsToMany(Tag, {
foreignKey: 'clientId',
otherKey: 'tagId',
through: models.Client_Tag,
as: 'tags'
});
const clientCount = await Client.findAll({
include: [ { model: Tags, as: 'tags' } ],
attributes: { exclude: 'tagId' }
});
Method2
const attr = {
name: {
type: DataTypes.STRING,
},
clientId: { // need clientId in tag model, and remove 'tagId' from client model
type: DataTypes.INTEGER,
}
};
const Tags = createModel('Tags', attr, {});
Client.belongsToMany(Tag, { foreignKey: 'tagId', as: 'tags' });
I have a query which is similar to the following.
const TODAY = new Date().setHours(0, 0, 0, 0);
const studentAttendances = await STUDENT_ATTENDANCES.findAll({
where: {
punch_in: { [Op.gt]: TODAY },
},
attributes: ['id', 'student_id', 'arrived_time'],
include: [
{
model: STUDENTS,
attributes: ['name'],
},
],
raw: true,
nest: true,
});
The current output given is an array of objects which look like the following.
{
"id": 1041,
"student_id": 16,
"arrived_time": "2019-05-29T08:29:41.000Z",
"student": {
"name": "Tom"
}
},
Instead of having a nested object as above how do i make the student name itself be an attribute of the main object ? Example as follows.
{
"id": 1041,
"student_id": 16,
"arrived_time": "2019-05-29T08:29:41.000Z",
"student": "Tom"
},
I hope to do this through sequelize without using any JS loops
Something like this should work, assuming your singular model name is "Student":
const studentAttendances = await STUDENT_ATTENDANCES.findAll({
where: {
punch_in: { [Op.gt]: TODAY },
},
attributes: [
[sequelize.col('Student.name'), 'studentName'], // will give you name as 'studentName'
'id', 'student_id', 'arrived_time'
],
include: [
{
model: STUDENTS,
attributes: [], // empty this out
},
]
});
I think you can handle it with pure javascript :
studentAttendances = studentAttendances.get({plain: true})
for(student in studentAttendances){
studentAttendances[student].student = studentAttendances[student].student.name
}
I have 2 different JSON object
JSON1
[
{
title: "Order Rule Violations",
type: "FieldView",
panel: "expanded",
data: [
{
label: "Rule Description",
type: "text",
},
{
label: "comments",
type: "inputarea",
},
],
},
]
JSON2
[
{
data: [
{
value: "00695",
},
{
value: " ",
},
],
},
]
I need to combine and get a result like
[
{
title: "Order Rule Violations",
type: "FieldView",
panel: "expanded",
data: [
{
label: "Rule Description",
type: "text",
value: "00695",
},
{
label: "comments",
type: "inputarea",
value: " ",
},
],
},
]
Please advise how can I achieve this using Angular / Typescript
This shall for this case:
for(var i=0;i<JSON1[0].data.length;i++)
JSON1[0].data[i].value= JSON2[0].data[i].value;
I altered JSON2 a little bit like JSON2 = ["","","",....] and the code below
worked for me ,
var k=0;
for(var i=0; i<this.JSON1.length; i++){
for(var j=0; j<this.JSON1[i].data.length; j++){
console.log("j",j);
this.JSON1[i].data[j].value = this.JSON2[k];
k++;
}
}
You can use use a nested Object.assign to iterate over each array and then merge the two objects.
const json1 = [{
title: 'Order Rule Violations',
type: 'FieldView',
panel: 'expanded',
data: [{
label: 'Rule Description',
type: 'text',
},
{
label: 'comments',
type: 'inputarea',
},
],
}];
const json2 = {
data: [{
value: '00695',
},
{
value: ' ',
},
]
};
const json3 = json1.map(y => {
y.data = y.data.map(
(x, index) => Object.assign(x, json2.data[index])
);
return y;
});
console.log(json3);
Another solution is to use Lodash's _.merge function
I have a php-script, which returns the following JSON
[
{
"Code":0,
"Message":"No problem"
},
{
"name":"o016561",
"status":1,
"locks":[
{
"ztn":"155320",
"dtn":"20131111",
"idn":"78"
},
{
"ztn":"155320",
"dtn":"20131111",
"idn":"91"
}
]
},
{
"name":"o011111",
"status":1,
"locks":[
{
"ztn":"155320",
"dtn":"20131111",
"idn":"91"
}
]
},
{
"name":"o019999",
"status":0,
"locks":[
]
},
{
"name":"o020000",
"status":0,
"locks":[
]
},
{
"name":"o020001",
"status":0,
"locks":[
]
}
]
Edit:
The grid should look something like this:
I've been able to load name and status into my grid - so far so good. But the more important part is, that I need the nested data in the locks-array being loaded into my grid, but I just can't get my code working. Any help would be appreciated.
I'm using ExtJS 4.2 if that matters.
Edit 2:
I tried
Ext.define("Locks", {
extend: 'Ext.data.Model',
fields: [
'ztn',
'dtn',
'idn'
]
});
Ext.define("ConnectionModel", {
extend: 'Ext.data.Model',
fields: ['name', 'status'],
hasMany: [{
model: 'Locks',
name: 'locks'
}]
});
var store = Ext.create('Ext.data.Store', {
model: "ConnectionModel",
autoLoad: true,
proxy: {
type: 'ajax',
reader: {
type: 'json',
root: 'name'
}
}
});
but it seemed to be wrong in multiple ways...
and it would be awesome if ztn and dtn could be displayed just seperated with a whitespace in the same column
You can add a renderer to the column. In that renderer you can do anything with the record...
Here's a working fiddle:
http://jsfiddle.net/Vandeplas/MWeGa/3/
Ext.create('Ext.grid.Panel', {
title: 'test',
store: store,
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Status',
dataIndex: 'status'
}, {
text: 'idn',
renderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
values = [];
record.locks().each(function(lock){
values.push(lock.get('idn'));
});
return values.join('<br\>');
}
}, {
text: 'ztn + dtn',
renderer: function (value, metaData, record, rowIdx, colIdx, store, view) {
values = [];
record.locks().each(function(lock){
values.push(lock.get('ztn') + ' ' + lock.get('dtn'));
});
return values.join('<br\>');
}
}],
height: 200,
width: 600,
renderTo: Ext.getBody()
});
Note
If you have control over your backend you better change the form of your data more like this:
{
"code": 0,
"message": "No problem",
"success": true,
"data": [
{
"name": "o016561",
"status": 1,
"locks": [
{
"ztn": "155320",
"dtn": "20131111",
"idn": "78"
},
{
"ztn": "155320",
"dtn": "20131111",
"idn": "91"
}
]
},
{
"name": "o011111",
"status": 1,
"locks": [
{
"ztn": "155320",
"dtn": "20131111",
"idn": "91"
}
]
}
]
}
That way you don't mix your control data (success, message, code,...) with your data and the proxy picks it up correctly (it can be a cause of the problems your experiencing). I added a success boolean => Ext picks it up and goes to the failure handler. It helps a lot with your exception handling.
Here is the proxy for it:
proxy: {
type: 'ajax',
api: {
read: ___URL____
},
reader: {
type: 'json',
root: 'data',
messageProperty: 'message'
}
}