Null properties are dropped during marshalling/unmarshalling with JSONIX - jsonix

I noticed that in my conversion from JSON to XML, if one of my JSON objects is null, the object is absent from the XML, for example
JSON
{
"name": {
"namespaceURI": "",
"localPart": "myApp",
"prefix": "",
"key": "myApp",
"string": "myApp"
},
"value": {
"TYPE_NAME": "generated.MyApp",
"inputData": [{
"TYPE_NAME": "generated.MyApp.InputData",
"dataType": "text",
"mandatory": false,
"length": 25,
"valid": true,
"value":null
}]
}
}
Convert to XML output (marshalString)
var context2 = new j.Context([generated2]);
var marshaller2 = context2.createMarshaller();
var objectAsXMLString2 = marshaller2.marshalString(template2);
<myApp>
<inputData>
<dataType>text</dataType>
<mandatory>false</mandatory>
<length>25</length>
<valid>true</valid>
</inputData>
</myApp>
Then converting back again to JSON (unmarshalString)
var unmarshaller2 = context2.createUnmarshaller();
var resultJSON2 = unmarshaller2.unmarshalString(objectAsXMLString2 );
{
"name": {
"namespaceURI": "",
"localPart": "myApp",
"prefix": "",
"key": "myApp",
"string": "myApp"
},
"value": {
"TYPE_NAME": "generated.MyApp",
"inputData": [{
"TYPE_NAME": "generated.MyApp.InputData",
"dataType": "text",
"mandatory": false,
"length": 25,
"valid": true
}]
}
}
So as you see, there is loss of information. When the complete cycle of conversion occurs, JSON -> XML -> back to JSON, some properties are not preserved. Anything that is null gets dropped - in this case it is the property 'value'. This is a problem for downstream applications that are expecting certain properties to be present on an object.
My XML schema that was used to generate the jsonix mapping object looks like this
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="myApp">
<xs:complexType>
<xs:sequence>
<xs:element name="inputData" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence >
<xs:element type="xs:string" name="dataType"/>
<xs:element type="xs:boolean" name="mandatory"/>
<xs:element type="xs:short" name="length"/>
<xs:element type="xs:string" name="elementId"/>
<xs:element type="xs:string" name="formId"/>
<xs:element type="xs:anySimpleType" name="value" />
<xs:element type="xs:boolean" name="valid"/>
<xs:element type="xs:short" name="precision"/>
<xs:element type="xs:short" name="scale"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
And generated mapping using jsonix-schema-compiler is
var generated_Module_Factory = function () {
var generated = {
name: 'generated',
typeInfos: [{
localName: 'MyApp',
typeName: null,
propertyInfos: [{
name: 'inputData',
minOccurs: 0,
collection: true,
elementName: {
localPart: 'inputData'
},
typeInfo: '.MyApp.InputData'
}]
}, {
localName: 'MyApp.InputData',
typeName: null,
propertyInfos: [{
name: 'dataType',
required: true,
elementName: {
localPart: 'dataType'
}
}, {
name: 'mandatory',
required: true,
elementName: {
localPart: 'mandatory'
},
typeInfo: 'Boolean'
}, {
name: 'length',
required: true,
elementName: {
localPart: 'length'
},
typeInfo: 'Short'
}, {
name: 'elementId',
required: true,
elementName: {
localPart: 'elementId'
}
}, {
name: 'formId',
required: true,
elementName: {
localPart: 'formId'
}
}, {
name: 'value',
required: true,
elementName: {
localPart: 'value'
},
typeInfo: 'AnySimpleType'
}, {
name: 'valid',
required: true,
elementName: {
localPart: 'valid'
},
typeInfo: 'Boolean'
}, {
name: 'precision',
required: true,
elementName: {
localPart: 'precision'
},
typeInfo: 'Short'
}, {
name: 'scale',
required: true,
elementName: {
localPart: 'scale'
},
typeInfo: 'Short'
}]
}],
elementInfos: [{
typeInfo: '.MyApp',
elementName: {
localPart: 'myApp'
}
}]
};
return {
generated: generated
};
};
if (typeof define === 'function' && define.amd) {
define([], generated_Module_Factory);
}
else {
var generated_Module = generated_Module_Factory();
if (typeof module !== 'undefined' && module.exports) {
module.exports.generated = generated_Module.generated;
}
else {
var generated = generated_Module.generated;
}
}
Is there a way to preserve all properties during the conversion, even if they are null?

Not at the moment.
I think what you're looking for is support for nillable elements as you probably want to distinguish null vs. absent property vs. property with the empty string as value.
Jsonix does not support nillable elements at the moment.

Related

Marshalling JSON to XML with jsonix gives invalide XML

I need to send a xml request to an api for my project. But for security reasons, I cannon't code direct xml in my program. So I decided to use jsonix to marshal JSON to xml, and unmarshal the respond of the request. But I have some problemes.
Here is my xml:
<?xml version = '1.0' encoding='UTF-8'?>
<api>
<authentication>
<login>MyLogin</login>
<password>MyPassword</password>
</authentication>
<command>
<myfunction>
<name>test</name>
</myfunction>
</command>
</api>
With this, I have generated the Mappings with the jsonix-schema-compiler module:
var Search_Module_Factory = function () {
var Search = {
name: "Search",
typeInfos: [{
localName: "Api.Command",
typeName: null,
propertyInfos: [{
name: "myFunction",
required: true,
elementName: {
localPart: "myfunction"
},
typeInfo: ".Api.Command.MyFunction"
}]
}, {
localName: "Api.Authentication",
typeName: null,
propertyInfos: [{
name: "login",
required: true,
elementName: {
localPart: "login"
}
}, {
name: "password",
required: true,
elementName: {
localPart: "password"
}
}]
}, {
localName: "Api.Command.MyFunction",
typeName: null,
propertyInfos: [{
name: "name",
required: true,
elementName: {
localPart: "name"
}
}]
}, {
localName: "Api",
typeName: null,
propertyInfos: [{
name: "authentication",
required: true,
elementName: {
localPart: "authentication"
},
typeInfo: ".Api.Authentication"
}, {
name: "command",
required: true,
elementName: {
localPart: "command"
},
typeInfo: ".Api.Command"
}]
}],
elementInfos: [{
typeInfo: ".Api",
elementName: {
localPart: "api"
}
}]
};
return {
Search: Search
};
};
if (typeof define === "function" && define.amd) {
define([], Search_Module_Factory);
}
else {
var Search_Module = Search_Module_Factory();
if (typeof module !== "undefined" && module.exports) {
module.exports.Search = Search_Module.Search;
}
else {
var Search = Search_Module.Search;
}
}
In my main code, I am marshalling the xml request like this:
var Search = require("./mappings/Search").Search;
var context = new Jsonix.Context([ Search]);
var marshaller = context.createMarshaller();
var originalJS = {
"api": {
"authentication": {
"login" :"mylogin",
"password": "mypassword",
},
"command":{
"myfunction":{
"name": "test"
}
},
}
};
var marshalledXML = marshaller.marshalString(originalJS);
console.log(marshalledXML);
And in the console.log(), instead of showing me something like this, which is the result I want:
<api><authentication><login>mylogin</login><password>mypassword</password></authentication><command><myfunction><name>test</name></myfunction></command></api>
It's returning something like this:
<api><authentication><login>mylogin</login><password>mypassword</password></authentication><command/></api>
The tag command is always empty, and I don't understand what I'm doing wrong.
Thank for your help

Export To Excel filtered data with Free jqgrid 4.15.4 in MVC

I have a question regarding Export to Excel in free-jqgrid 4.15.4. I want to know how to use this resultset {"groupOp":"AND","rules":[{"field":"FirstName","op":"eq","data":"Amit"}]} into my Business Logic Method.
Just for more clarification, I've using OfficeOpenXml and if I don't use filtered resultset(aforementioned) it is working fine and I'm able to download file with full records in an excel sheet. But I'm not sure what to do or how to utilize the resultset {"groupOp":"AND","rules":[{"field":"FirstName","op":"eq","data":"Amit"}]}
If required I can share my controller and BL code.
I have added a fiddle which shows implementation of Export to Excel button in jqGrid pager.
Before coming to here, I've read and tried to understand from following questions:
1] jqgrid, export to excel (with current filter post data) in an asp.net-mvc site
2] Export jqgrid filtered data as excel or CSV
Here is the code :
$(function () {
"use strict";
var mydata = [
{ id: "10", FirstName: "test", LastName: "TNT", Gender: "Male" },
{ id: "11", FirstName: "test2", LastName: "ADXC", Gender: "Male" },
{ id: "12", FirstName: "test3", LastName: "SDR", Gender: "Female" },
{ id: "13", FirstName: "test4", LastName: "234", Gender: "Male" },
{ id: "14", FirstName: "test5", LastName: "DAS", Gender: "Male" },
];
$("#list").jqGrid({
data: mydata,
colNames: ['Id', 'First Name', 'Last Name', 'Gender'],
colModel: [
{
label: "Id",
name: 'Id',
hidden: true,
search: false,
},
{
label: "FirstName",
name: 'FirstName',
searchoptions: {
searchOperators: true,
sopt: ['eq', 'ne', 'lt', 'le','ni', 'ew', 'en', 'cn', 'nc'],
}, search: true,
},
{
label: "LastName",
name: 'LastName',
searchoptions: {
searchOperators: true,
sopt: ['eq', 'ne', 'lt', 'ni', 'ew', 'en', 'cn', 'nc'],
}, search: true,
},
{
label: "Gender",
name: 'Gender',
search: true, edittype: 'select', editoptions: { value: 'Male:Male;Female:Female' }, stype: 'select',
},
],
onSelectRow: function (id) {
if (id && id !== lastsel) {
jQuery('#list').restoreRow(lastsel);
jQuery('#list').editRow(id, true);
lastsel = id;
}
},
loadComplete: function (id) {
if ($('#list').getGridParam('records') === 0) {
//$('#grid tbody').html("<div style='padding:6px;background:#D8D8D8;'>No records found</div>");
}
else {
var lastsel = 0;
if (id && id !== lastsel) {
jQuery('#list').restoreRow(lastsel);
jQuery('#list').editRow(id, true);
lastsel = id;
}
}
},
loadonce: true,
viewrecords: true,
gridview: true,
width: 'auto',
height: '150px',
emptyrecords: "No records to display",
iconSet:'fontAwesome',
pager: true,
jsonReader:
{
root: "rows",
page: "page",
total: "total",
records: "records",
repeatitems: false,
Id: "Id"
},
});
jQuery("#list").jqGrid("navButtonAdd", {
caption: "",
buttonicon: "fa-table",
title: "Export To Excel",
onClickButton: function (e) {
var projectId = null;
var isFilterAreUsed = $('#grid').jqGrid('getGridParam', 'search'),
filters = $('#grid').jqGrid('getGridParam', 'postData').filters;
var Urls = "/UsersView/ExportToExcel_xlsxFormat?filters="+ encodeURIComponent(filters); //' + encodeURIComponent(filters);/
if (totalRecordsCount > 0) {
$.ajax({
url: Urls,
type: "POST",
//contentType: "application/json; charset=utf-8",
data: { "searchcriteria": filters, "projectId": projectId, "PageName": "MajorsView" },
//datatype: "json",
success: function (data) {
if (true) {
window.location = '/UsersView/SentFiletoClientMachine?file=' + data.filename;
}
else {
$("#resultDiv").html(data.errorMessage);
$("#resultDiv").addClass("text-danger");
}
},
error: function (ex) {
common.handleAjaxError(ex.status);
}
});
}
else {
bootbox.alert("There are no rows to export in the Participant List")
if (dialog) {
dialog.modal('hide');
}
}
}
});
});
https://jsfiddle.net/ap43xecs/10/
There are exist many option to solve the problem. The simplest one consist of sending ids of filtered rows to the server instead of sending filters parameter. Free jqGrid supports lastSelectedData parameter and thus you can use $('#grid').jqGrid('getGridParam', 'lastSelectedData') to get the array with items sorted and filtered corresponds to the current filter and sorting criteria. Every item of the returned array should contain Id property (or id property) which you can use on the server side to filter the data before exporting.
The second option would be to implement server side filtering based on the filters parameter, which you send currently to the server. The old answer (see FilterObjectSet) provides an example of filtering in case of usage Entity Framework. By the way, the answer and another one contain code, which I used for exporting grid data to Excel using Open XML SDK. You can compare it with your existing code.
In some situations it could be interesting to export grid data to Excel without writing any server code. The corresponding demo could be found in the issue and UPDATED part of the answer.

populate mongoose co-related schema for tree-view

I have created following mongoose schema
workspace-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator');
var workspaceSchema=new mongoose.Schema({
name:{type: String, lowercase: true, required: [true, "can't be blank"],
//match: [/^[a-zA-Z0-9]$/, 'is invalid'],
index: true,
unique: true
},
processes:[{type:mongoose.Schema.Types.ObjectId,ref:'Process'}],
},{timestamp:true})
workspaceSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Workspace',workspaceSchema)
process-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator')
var processSchema=new mongoose.Schema({
name:{type: String, lowercase: true, required: [true, "can't be blank"],
//match: [/^[a-zA-Z0-9]$/, 'is invalid'],
index: true,
unique: true
},
state:String, //enabled/disabled/started/stopped/checkin/checkout/starting/stopping
queues:[{type:mongoose.Schema.Types.ObjectId,ref:'Queue'}],
workspace:{type:mongoose.Schema.Types.ObjectId,ref:'Workspace'}
},{timestamp:true})
processSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Process',processSchema)
queue-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator');
var queueSchema=new mongoose.Schema({
name:{type: String, lowercase: true, required: [true, "can't be blank"],
//match: [/^[a-zA-Z0-9]$/, 'is invalid'],
index: true,
unique: true
},
type:String, //start//end/custom/decision/split/join
prev:{type:mongoose.Schema.Types.ObjectId,ref:'Queue'},
next:{type:mongoose.Schema.Types.ObjectId,ref:'Queue'},
workitems:[{type:mongoose.Schema.Types.ObjectId,ref:'Workitem'}],
process:{type:mongoose.Schema.Types.ObjectId,ref:'Process'}
},{timestamp:true})
queueSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Queue',queueSchema)
workitem-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator');
var workitemSchema=new mongoose.Schema({
status:String, //locked//processing//done//available
lockedby:String,
queue:{type:mongoose.Schema.Types.ObjectId,ref:'Queue'},
process:{type:mongoose.Schema.Types.ObjectId,ref:'Process'}
},{timestamp:true})
//workitemSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Workitem',workitemSchema)
like to populate the data in the following tree-view format
workspace ->[process]-->[queue]-->[workitem]
tried following but all sub paths and array items) are not getting populating.
...
var Process=require('./models/process-model')
var Workspace=require('./models/workspace-model')
var Workitem=require('./models/workitem-model')
var Queue=require('./models/queue-model')
Workspace.findOne({name:'dummy'}).populate({
path: 'processes',
// Get friends of friends - populate the 'friends' array for every friend
populate: { path: 'queues' }
}).exec().then((data)=>{console.log(data})
following is sample data file..
workspace
{
"_id": ObjectId("59f313665087dc2648ab2dc1"),
"name": "dummy",
"processes": [
ObjectId("59f313665087dc2648ab2dc2")
],
"__v": 1
}
process {
"_id": ObjectId("59f313665087dc2648ab2dc2"),
"workspace": ObjectId("59f313665087dc2648ab2dc1"),
"state": "enabled",
"name": "outward",
"queues": [
ObjectId("59f3312ee9bbaa27e032f55b"),
ObjectId("59f3312ee9bbaa27e032f55c"),
ObjectId("59f3312ee9bbaa27e032f55d")
],
"__v": 1
}
queue {
"_id": ObjectId("59f3312ee9bbaa27e032f55c"),
"process": ObjectId("59f313665087dc2648ab2dc2"),
"type": "custom",
"name": "data entry",
"workitems": [],
"__v": 0,
"next": ObjectId("59f3312ee9bbaa27e032f55d"),
"prev": ObjectId("59f3312ee9bbaa27e032f55b")
} {
"_id": ObjectId("59f3312ee9bbaa27e032f55d"),
"process": ObjectId("59f313665087dc2648ab2dc2"),
"type": "end",
"name": "work exit",
"workitems": [],
"__v": 0,
"prev": ObjectId("59f3312ee9bbaa27e032f55c")
} {
"_id": ObjectId("59f3312ee9bbaa27e032f55b"),
"process": ObjectId("59f313665087dc2648ab2dc2"),
"type": "start",
"name": "work introduction",
"workitems": [
ObjectId("59f33b3b99506b25f033ecf5")
],
"__v": 1,
"next": ObjectId("59f3312ee9bbaa27e032f55c")
}
workitem {
"_id": ObjectId("59f33b3b99506b25f033ecf5"),
"status": "available",
"__v": 0,
"process": ObjectId("59f313665087dc2648ab2dc2"),
"queue": ObjectId("59f3312ee9bbaa27e032f55b")
}
Please help .
Thanks in advance..
The problem was that the console.log was unable to print after 2-3 nested level.
solution is as follows
var util = require('util')
.....
//following line will print nested objects
console.log(util.inspect(data, false, null))
....

How to set allowSorting false for particular column after IgGrid getting loaded?

I have globalized this below Grid initialization as a common function. I have used sorting as default for all columns. I need to set "allowSorting" for particular column after IgGrid getting loaded. I have added a sample code here. I am unable to set "allowSorting" in this variable "colSettings".
$("#gridSorting").igGrid({
primaryKey: "ProductID",
columns: [
{ headerText: "Product ID", key: "ProductID", dataType: "number" },
{ headerText: "Product Name", key: "Name", dataType: "string", template: '<a id="name${ProductID}">${Name}</a>' },
{ headerText: "Product Number", key: "ProductNumber", dataType: "string", template: '<input type="text" class="txtBox" id="${ProductID}" value="" />' },
{ headerText: "Product Key", key: "ProductKey", dataType: "string", template: '<input type="text" class="txtBox1" maxlength="4" id="${ProductKey}" value="${ProductKey}" />' }
],
features: [
{
name: "RowSelectors",
enableCheckBoxes: true,
enableRowNumbering: false
},
{
name: "Selection",
mode: 'row',
multipleSelection: true
},
{
name: "Sorting",
type: "local"
}
],
width: "500px",
dataSource: products
});
var colSettings = [
{
columnKey: 'ProductID',
allowSorting: true
},
{
columnKey: 'Name',
allowSorting: true
},
{
columnKey: 'ProductNumber',
allowSorting: false
}
];
$("#gridSorting").igGridSorting("option", "columnSettings", colSettings);
Please advise me how to set "allowSorting: false" for particular column in this example.

Sails JS Waterline join of multiple models

Hi i'm trying to join multiple tables with populate method, i googled and couldn't find
efficient way of doing it, i do not want to query db several times to build the result, is it possible to solve it with sails version "~0.10.0-rc7" i'm building quit big project with more then hundred of tables.
var co = {
adapter: 'someMysqlServer',
migrate:'safe',
autoCreatedAt: false,
autoUpdatedAt: false,
autoPK:false,
attributes:{
id:{
type:"int",
primaryKey: true,
autoIncrement: true
},
code:"string",
priority :"int",
co_group_c_id :"int",
timezone_c_id :"int",
lang_c_id :"int",
currency_c_id :"int",
name_used :"string",
name_official :"string",
co_tax_no :"int",
co_vat_no :"int",
co_vat_reg :"int",
co_reg_record :"string",
co_representative :"string",
co_addresses:{
collection: "co_address",
via: "co_user_id"
},
}
};
module.exports = co;
var co_address = {
adapter: 'someMysqlServer',
migrate:'safe',
autoCreatedAt: false,
autoUpdatedAt: false,
autoPK:false,
attributes: {
id:{
type:"int",
primaryKey: true,
autoIncrement: true,
},
address_c_id:"int" ,
address_street_first: "string",
address_street_second: "int",
address_street_third: "int",
address_postalcode: "string",
address_city: "string",
co_user_id: {
model: 'co_user'
},
co_id: {
model: 'co'
},
co_address_opening_hours:{
collection: "co_address_opening_hours",
via: "co_address_id"
},
}
};
module.exports = co_address;
var co_address_opening_hours = {
adapter: 'someMysqlServer',
migrate:'safe',
autoCreatedAt: false,
autoUpdatedAt: false,
autoPK:false,
attributes:{
id:{
type:"int",
primaryKey: true,
autoIncrement: true
},
day_c_id: "int",
time_from: "datetime",
time_to :"datetime",
co_address_id: {
model: 'co_address'
}
}
};
module.exports = co_address_opening_hours;
//controller
get_co:function(req,res){
co.find()
.populate("co_addresses")
.populate("co_address_opening_hours")
.exec(function(e, company) {
if(e) console.log(e);
console.log(company);
res.json(company);
})
In SailsJS 0.10+ you can use model associations to do database joins. You can read more about them here: http://sailsjs.com/documentation/concepts/models-and-orm/associations
Basically you first define an association in your model;
var someModel = {
attributes: {
name: {
type: 'text'
}
}
};
var someOtherModel = {
attributes: {
name: {
type: 'text'
},
associationProp: {
model: 'someModel'
}
}
};
In the code above someOtherModel contains association (relation) to someModel. To do a join query you can use .populate() method. For example retrieve all someOtherModel entities and populate associative properties;
someOtherModel.find().populate('associationProp').exec(...);
For MySQL and PSQL adapters there's also .query() method available where you can write some hand written SQL queries to be executed (this also works in sails <0.10);
Model.query(<sql query>, <optional data>, callback);

Resources