JSONIX: Get restrictions and default value for properties - javascript

I am using JSONIX for marshalling and unmarshalling XML files. So far it works pretty well. What I am missing is the possibility to get default values and restrictions like minOccours and maxOccours-Values. Is this somehow possible with JSONIX?
These properties:
<xsd:sequence>
<xsd:element name="inflowMin" type="framework:flowType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="inflowMax" type="framework:flowType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="unitOfFlowControl" type="framework:flowUnit" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="waterCosts" type="xsd:double" default="0.0"/>
<xsd:attribute name="controllable" type="xsd:boolean" default="0"/>
<xsd:attribute name="scalingOfControl" type="xsd:double" default="1.0" />
Get:
propertyInfos: [{
type: 'element',
name: 'inflowMin',
elementName: 'inflowMin',
typeInfo: ...
}, {
type: 'element',
name: 'inflowMax',
elementName: 'inflowMax',
typeInfo: ...
}, {
type: 'element',
name: 'unitOfFlowControl',
elementName: 'unitOfFlowControl',
typeInfo: 'String'
}, {
name: 'waterCosts',
typeInfo: 'Double',
attributeName: 'waterCosts',
type: 'attribute'
}, {
name: 'controllable',
typeInfo: 'Boolean',
attributeName: 'controllable',
type: 'attribute'
}, {
name: 'scalingOfControl',
typeInfo: 'Double',
attributeName: 'scalingOfControl',
type: 'attribute'
}]
}
Thanks!

The feature you requested is now implemented in Jsonix 2.3.2 and Jsonix Schema Compiler 2.3.7.
Jsonix Schema Compiler now produces required, minOccurs and maxOccurs in the generated mappings and the JSON schema.
This is how it looks in the mappings:
{
localName: 'Items',
propertyInfos: [{
name: 'item',
minOccurs: 0,
maxOccurs: 100,
collection: true,
elementName: {
localPart: 'item'
},
typeInfo: '.Items.Item'
}]
}
And in the JSON Schema:
"Items":{
"type":"object",
"title":"Items",
"properties":{
"item":{
"title":"item",
"allOf":[
{
"type":"array",
"items":{
"$ref":"#/definitions/Items.Item"
},
"maxItems":100,
"minItems":0
}
],
"propertyType":"element",
"elementName":{
"localPart":"item",
"namespaceURI":""
}
}
},
"typeType":"classInfo",
"typeName":{
"localPart":"Items",
"namespaceURI":""
},
"propertiesOrder":[
"item"
]
}
You can access this metadata from the Jsonix context as follows:
var context = new Jsonix.Context([ PO ]);
var itemsClassInfo = context.getTypeInfoByName("PO.Items");
var itemPropertyInfo = itemsClassInfo.getPropertyInfoByName("item");
test.equal(false, itemPropertyInfo.required);
test.equal(0, itemPropertyInfo.minOccurs);
test.equal(100, itemPropertyInfo.maxOccurs);
test.done();

Disclaimer: I'm the author.
At the moment not, this information is not generated yet. There was this issue back then, but it was not implemented.
If you're interested in this functionality, please file two issues here (one for the default value and the other one for minOccurs/maxOccurs).
In principle, this information is available from the XML Schema, but in some cases it is not clearly mappable to the generated model. In a few weird cases like repeatable choice or sequence this won't work, but in most cases it will. So it is implementable, please file the issues.
Do you just need these things in the generated mappings? Or some kind of API to access it?

Related

How to apply a type definition in a template literal during looping?

I am looping through a list of objects (given below)
let links = [
{
name: 'Services',
children: [
{
service1: 'First service',
caption: 'We provide a full range of audit and assurance services to a wide range of clients',
},
{
service2:'Second service',
caption: 'Second caption'
}
]
}
]
To access the service1 and service2, I use index during the looping such that children[`service${index+1}`] Typescript shows the error that Element implicitly has an 'any' type because expression of type
`service${number}`
can't be used to index.
How can I solve this?

Apollo Client replaces an array of objects with the same id and different values with an array of copies of the first object

Our GraphQL server responds to a query with data that includes an array of objects each of which shares the same id and different values for a different key. For instance, we might have an array that looks like:
[
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 6 },
{ id: 123, name: 'foo', type: 'bar', cost: 7 },
{ id: 123, name: 'foo', type: 'bar', cost: 8 }
]
We can see in the Network tab that the response from the server has the correct data in it. However, by the time it goes through processing by the Apollo Client module the array has been transformed into something that might look like this:
[
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 5 },
{ id: 123, name: 'foo', type: 'bar', cost: 5 }
]
Essentially what we're seeing is that if all of the objects in an array share the same value for id then all objects in the array become copies of the first object in the array.
Is this the intended behavior of Apollo Client? We thought maybe it had something to do with incorrect caching, but we were also wondering if maybe Apollo Client assumed that subsequent array members with the same id were the same object.
It looks like this is behavior as intended. The Apollo Client normalizes on id.
As the other answer suggests this happens because Apollo normalises by ID. There's a very extensive article on the official blog that explains the rationale of it, along with the underlying mechanisms.
In short, as seen by Apollo's cache, your array of objects contains 4 instances of the same Object (id 123). Same ID, same object.
This is a fair assumption on Apollo's side, but not so much in your case.
You have to explicitly tell Apollo that these are indeed 4 different items that should be treated differently.
In the past we used dataIdFromObject, and you can see an example here.
Today, you would use typePolicies and keyfields:
const cache = new InMemoryCache({
typePolicies: {
YourItem: {
// Combine the fields that make your item unique
keyFields: ['id', 'cost'],
}
},
});
Docs
It works for me:
const cache: InMemoryCache = new InMemoryCache({ dataIdFromObject: o => false )};
previous answer solves this problem too!
Also you can change the key name(for example id => itemId) on back-end side and there won't be any issue!
I have the same issue. My solution is to set fetchPolicy: "no-cache" just for this single API so you don't have to change the InMemoryCache.
Note that setting fetchPolicy to network-only is insufficient because it still uses the cache.
fetchPolicy document

Sench Touch 2 get container data /store dynamically

I want to get container data dynamically. Can any one help me to display the container items dynamically. The date or store url may be some external page, I want to get the container store/date using MVC. Can any help me?
Below is my code
Ext.define('Mvcapp.view.LayoutList',{
extend: 'Ext.Container',
xtype: 'layoutlist',
config:{
title: 'Layout',
iconCls:'star',
styleHtmlContent: true,
items:[{
data: [{
fname: 'Stratton',
lname: 'Sclavos',
role: 'Executive Chairman'
}, {
fname: 'Michael',
lname: 'Mullany',
role: 'CEO'
}, {
fname: 'Ted',
lname: 'Driscoll',
role: 'Vice President Worldwide Sales'
}, {
fname: 'Abraham',
lname: 'Elias',
role: 'Chief Technical Officer'
}, {
fname: 'Jeff',
lname: 'Hartley',
role: 'Vice President of Services and Training'
}, {
fname: 'Adam',
lname: 'Mishcon',
role: 'Vice President of Operations'
}, {
fname: 'Judy',
lname: 'Lin',
role: 'Vice President of Engineering'
}], // data
tpl: '<tpl for="."><div style="float:left;width:300px;"><strong>{lname}</strong>, {fname} <em class="muted">({role})</em></div></tpl>'
}]
}
});
Things you need to do is, first define a model having required fields. Next create a store and set model config to previously defined. While defining the store you need to choose from various proxies sencha touch has give. If you are building app that is to be built for mobile phones then there's not much choice to make. You simply have to use JsonP proxy.
Here's what I'd do -
var listitem=Ext.define('ListItem', {
extend: 'Ext.data.Model',
config: {
fields: ['fname','lname','role']
}
});
var store = Ext.create('Ext.data.Store', {
model: listitem,
autoLoad: true,
proxy: {
type: 'jsonp',
url: 'http://localhost/json_feed_url.php',
reader: {
type: 'json',
rootProperty: 'data'
}
}
});
var myList = Ext.create('Ext.List', {
styleHtmlContent:true,
store:store,
itemTpl:['<div style="float:left;width:300px;"><strong>{lname}</strong>, {fname} <em class="muted">({role})</em></div>']
});
Ext.Viewport.add(myList);
As you can see, i first defined model for our store. Store is created using JsonP proxy and it is set to load automatically. I've also set up reader, that will read the response received from server and parse it. I don't have to worry about it anymore now because all I've to do it just set the rootPropery.
Next a list is created and previously defined store is assigned in it's config. So whenever this piece of code is run, store will get data from server and display it in list. Guess that's what you want.
To start, just put this piece inside launch method of your app.js and you're good to go. In case you want php code here's it -
<?php header('Content-type:application/javascript');
$items =array();
$items[] = array('fname'=>'A','lname'=>'B','role'=>1);
$items[] = array('fname'=>'C','lname'=>'D','role'=>2);
$items[] = array('fname'=>'E','lname'=>'F','role'=>3);
$items[] = array('fname'=>'G','lname'=>'H','role'=>4);
$items[] = array('fname'=>'I','lname'=>'J','role'=>5);
print $_GET['callback'].'('.json_encode(array('data'=>$items)) .')';
?>
I'm familiar with php n all, but you can use whatever suits you. Idea is same. :D

Programmatically Set Constructor Parameters in Javascript

I am trying to interact with a javascript api (bare in mind I have never done this before). An example of what I am attempting to work with is here:
SearchSpring.Catalog.init({
leaveInitialResults : true,
facets : '.leftNav',
results : '#results',
result_layout: 'list',
results_per_page : 12,
layout: 'top',
loadCSS: false,
filters: {
color: ['Blue']
},
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens']
},
maxFacets: 5,
maxFacetOptions: 10,
sortText: 'Sort By ',
sortType: 'dropdown',
filterText: 'Refine Search Results',
previousText: 'Previous',
scrollType: 'scroll',
scrollTo: 'body',
backgroundSortField: 'price',
backgroundSortDir: 'desc',
compareText: 'Compare Items',
summaryText: 'Current Filters',
showSummary: true,
subSearchText: 'Subsearch:',
showSubSearch: true,
forwardSingle: false,
afterResultsChange: function() { $('.pagination').hide(); },
filterData: function(data) { console.debug(data); }
});
In the example I want to add a "backgroundFilter" to this with a value:
var cat="MyNewCategory";
cat.value="ANewValue;
How would I add this category and value to the backgroundFilters: listed above?
This is a very common framework initialization pattern when working with frameworks.
Your example code is passing a JavaScript Object {} as a parameter into a function () that is called init.
Taking out all definitions the pattern looks like this:
SomeFramework.frameworkFunction({});
In the above code the {} is an empty object used for initialization. There are two ways that you can work with that object in practice.
Regarding your first code snippet, you can add code into that 'object literal'.
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens'],
cat: ['My value']
},
Notice the added comma, this is an important tripping point. This may or may not fit your needs, depending on a few factors.
Regarding your second code snippet, you can apply members to JavaScript objects at runtime. What I mean is, your var cat can be added to the anonymous object-literal that is being passed in. Hard to say, but a simple concept. Here is how:
//Say this is initialized in some separate way. //There is a bug here I'll describe later.
var cat="MyNewCategory";
cat.value="ANewValue";
//Extract and name the initialization object. It is verbatim at this point.
var initObject = {
leaveInitialResults : true,
facets : '.leftNav',
results : '#results',
result_layout: 'list',
results_per_page : 12,
layout: 'top',
loadCSS: false,
filters: {
color: ['Blue']
},
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens']
},
maxFacets: 5,
maxFacetOptions: 10,
sortText: 'Sort By ',
sortType: 'dropdown',
filterText: 'Refine Search Results',
previousText: 'Previous',
scrollType: 'scroll',
scrollTo: 'body',
backgroundSortField: 'price',
backgroundSortDir: 'desc',
compareText: 'Compare Items',
summaryText: 'Current Filters',
showSummary: true,
subSearchText: 'Subsearch:',
showSubSearch: true,
forwardSingle: false,
afterResultsChange: function() { $('.pagination').hide(); },
filterData: function(data) { console.debug(data); }
};
//Now we can add variables (and functions) dynamically at runtime.
initObject.cat = cat;
//And pass them into the framework initialization in a separated way.
SearchSpring.Catalog.init(initObject);
Now for the bug. I don't know the solution because I do not know what it is intended to do, but I can point out what is potentially incorrect.
var cat="MyNewCategory";
cat.value="ANewValue;
This code is: 1 creating a String Object called cat. 2 changing the value to a new string.
I do not think this is what you really want.
To add a new backgroundFilter, in the separated way above, it would be:
initObject.backgroundFilters.cat = ['A', 'B'];
//Line above would give you this type of definition within the initObject (at runtime):
backgroundFilters: {
category: ['Shirt', 'Shoes'],
department: ['Mens'],
cat: ['A','B']
},
For this to work it will depend on what the framework is expecting regarding backgroundFilters.
Hope that helps.
All the best!
Nash
I don't quite understand - do you want to have the backgroundFilters categories as structured objects rather than plain strings? If you are in control of the entire API, you can do something like
...
backgroundFilters: {
category: [
new SearchSpring.Catalog.Category("Shirt"),
new SearchSpring.Catalog.Category("Shoes"),
new SearchSpring.Catalog.Category("MyNewCategory", "ANewValue")
],
department: 'Mens'
}
...

defining relationships in Backbone-Relational -- not sure which direction the options are pointing?

I'm having a heckuva time understanding the documentation for Backbone-Relational; it's not 100% clear on which relation to add things like includeInJSON. Probably best to describe my confusion by illustrating the structure I'm trying to create. I have a Venue model that has zero or more nested Address models (1:n relationship). The backend store is MongoDB, which can have embedded objects. I'd like to store it in this format:
{
id: 12345,
label: 'OPUS Cafe Bistro',
addresses: [
{
type: 'mailing',
address1: '#52 - 650 Duncan Ave',
city: 'Penticton, BC'
},
{
type: 'main',
address1: '#106 - 1475 Fairview Rd',
city: 'Penticton, BC'
}
]
}
(Please ignore the ugly data structures; I've adjusted it for brevity.) Now I believe I set up the relationship between Venue and Address thusly:
var Venue = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasMany,
key: 'addresses',
relatedModel: 'Address',
includeInJSON: false,
collectionType: 'Addresses',
reverseRelation: {
key: 'venue'
}
}
});
If I understand correctly, I set includeInJSON to false in order to prevent the Venue from being serialised into the venue key in Address, but under reverseRelation I leave includeInJSON blank in order to have the full Address (without a venue property) serialised as an array in the addresses property of the Venue – as per my hoped-for example. Correct?
By the same token, I'm trying to understand the same concept in relation to a join-style relationship. Consider that Venue now has an organisationID field:
/* venue in JSON format */
{
id: 12345,
organisationID: 336,
label: 'OPUS Cafe Bistro',
addresses: []
}
/* and now for the organisation */
{
id: 336,
label: 'OPUS Entertainment Group'
}
Using the examples in the documentation, which seem to prefer the Backbone.HasMany relationship, I think that I'd have to set up Organisation thus:
var Organisation = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone:HasMany,
key: 'venues',
relatedModel: 'Venue',
includeInJSON: Backbone.Model.prototype.idAttribute,
collectionType: 'Venues',
reverseRelation: {
key: 'organisationID',
includeInJSON: false
}
}
]
});
... which should serialise into the above example, right? (I.e., Venue grabs Organisation's id and inserts it into organisationID, and Organisation doesn't serialise any list of Venues)
Thanks in advance for any help – looking forward to using this handy library, after clawing my eyeballs out trying to write my own relational glue for Backbone.js :-)
(copying my answer from https://github.com/PaulUithol/Backbone-relational/issues/37 over here, so more people can hopefully find it)
I'm sure you've figured it out by now, sorry for replying so late, but just to be sure: options (like collectionType and includeInJSON) apply to the key that's within the same object.
So, for your first example, you could write the relation as:
var Venue = Backbone.RelationalModel.extend({
relations: [
{
type: Backbone.HasMany,
key: 'addresses',
relatedModel: 'Address',
includeInJSON: true,
collectionType: 'Addresses',
reverseRelation: {
key: 'venue',
includeInJSON: 'id'
}
}
});
This will create a Venue that HasMany addresses. The addresses key for any given Venue uses the collectionType Addresses, and is fully serialized because includeInJSON is set to true (not that the default value for includeInJSON is also true; so if you don't specifiy it, it will fully serialize a relation or reverseRelation).
The reverseRelation simply takes the same options, only applies them with respect to the venue key on any Address. In this case, it will only serialize the venue.id property for the venue it's linked to.

Categories