json in xpages - kwintessential notes · pdf filejson in xpages ... json or javascript object...

22
JSON in XPages Overview & getting started Patrick Kwinten

Upload: phungthu

Post on 08-Mar-2018

270 views

Category:

Documents


9 download

TRANSCRIPT

��

JSON in XPages Overview & getting started

Patrick Kwinten

Contents

Introduction............................................................................................................................................. 2

Document version ................................................................................................................................ 2

Terminology ............................................................................................................................................ 2

JSON ........................................................................................................................................................ 2

Example JSON Object ........................................................................................................................... 2

JavaScript classes and objects .............................................................................................................. 3

Example ........................................................................................................................................... 3

Ways to put functions into JSON objects. ......................................................................................... 4

JSON and Domino .................................................................................................................................... 5

NotesView - ReadViewEntries .............................................................................................................. 5

Optional arguments - Outputformat=JSON ....................................................................................... 5

Other sources of JSON in Domino ......................................................................................................... 5

ViewColumn ..................................................................................................................................... 5

Fields ............................................................................................................................................... 6

JSON in XPages ........................................................................................................................................ 7

Use of Scope Variables ......................................................................................................................... 7

Example working on client side with server side data ....................................................................... 7

Example Repeat control ....................................................................................................................... 8

Example Data Table control ................................................................................................................. 9

Example – XAgent that collects column values ................................................................................... 11

Use of REST Service ............................................................................................................................ 14

Step 1 – Set up the REST Service control ......................................................................................... 14

Example Write JSON to field ............................................................................................................... 15

Display the values .......................................................................................................................... 15

Edit the values................................................................................................................................ 17

Result ............................................................................................................................................. 20

Recommended resources ...................................................................................................................... 21

Introduction

This document describes JSON and how the use it with XPages development. On a high level JSON is

described and on a low level the usage within XPages is demonstrated.

After reading this document you, as a developer, should decide if JSON can be beneficial in your projects.

This document might give you guidance in taking the first steps.

Document version

Date Version Author Comments

2013-09-17 0.1 Patrick Kwinten Initial Draft

2013-09-17 0.2 Patrick Kwinten Added JSON in Notes fields

Terminology

Below is a list with technical terms used in througho ut this document:

Term Description

XPages Rapid web and mobile application development

technology.

XAgent Web Agents XPages style.

XPinC XPages in Notes client

SSJS Server-side JavaScript

Extension Library Library that provides additional controls for

XPages.

JSON

From Wikipedia:

JSON or JavaScript Object Notation, is a text-based open standard designed for human-readable data

interchange. It is derived from the JavaScript scripting language for representing simple data structures

and associative arrays, called objects. Despite its relationship to JavaScript, it is language-independent,

with parsers available for many languages.

The JSON format is often used for serializing and transmitting structured data over a network

connection. It is used primarily to transmit data between a server and web application, serving as an

alternative to XML.

Example JSON Object

The following example shows the JSON representation of an object that describes a person. The object

has string fields for first name and last name, a number field for age, an object representing the person's

address and an array of phone number objects.

{

"firstName": "John",

"lastName": "Smith",

"age": 25,

"address": {

"streetAddress": "21 2nd Street",

"city": "New York",

"state": "NY",

"postalCode": 10021

},

"phoneNumbers": [

{

"type": "home",

"number": "212 555-1234"

},

{

"type": "fax",

"number": "646 555-4567"

}

]

}

JavaScript classes and objects

JavaScript is an object-based language. JSON is an example of that.

Classes can have:

• Properties.

• Methods, e.g. getFullName().

JSON objects can include functions in the same way they can contain arrays and objects. These functions

are then methods of the JSON object.

Objects are created from these classes with the "new" statement.

Example function Person(fName, mName, lName,phone,eMail) {

this.FirstName = (fName) ? fName : “”;

this.MiddleName = (mName) ? mName : “”;

this.LastName = (lName) ? lName : “”;

this.phone = (phone) ? phone : “”;

this.email = (eMail) ? EMail : “”;

this.getFullName = function() {

var tempName = [];

if (this.FirstName != “”) tempName.push(this.FirstName);

if (this.MiddleName != “”) tempName.push(this.MiddleName);

if (this.LastName != “”) tempName.push(this.LastName);

return tempName.join(“ ”);

}

}

You create a new person object as followed:

var p1 = new Person(“Patrick”, “”, “Kwnten”, “188800”, “[email protected]”);

And use it:

alert(“The person's name is ” + p1.FirstName + “ ” + p1.LastName + “.”);

Ways to put functions into JSON objects.

• In-line as the object is declared:

var myObj = {

myData : “some data goes here”,

myFunct : function (parms) {

// CODE OF THE FUNCTION

this.myData = “some new data is here”;

return something; //optional

}

};

• Externally after the function is declared:

var myObj = {

myData : “some data goes here”

};

myObj.myFunct = function (parms) {

// CODE OF THE FUNCTION

this.myData = “some new data is here”;

return something; //optional

};

JSON and Domino Domino has by default capabilities to produce JSON.

NotesView - ReadViewEntries

Use this command to access view data in XML form without appearance attributes such as fonts, list

separators, date formats, HTML settings, view templates and frame redirections.

Optional arguments - Outputformat=JSON

This argument displays the view in a JavaScript Object Notation format.

In this JSON object there is 1 (toplevel) viewentry node that contains all the entries. Similarly there's only

1 entrydata per document.

In most cases you cannot bind this JSON object directly to a control in XPages. Therefor other

alternatives could be considered.

Other sources of JSON in Domino

In Domino there are other design elements that can produce JSON code:

• View Columns.

• Forms and pages.

• Fields and computed text areas.

• Agents.

• Script libraries.

• File attachments.

o Both in the database and in documents.

• XPages “agents”.

• Server-side JavaScript (XPages only).

• @Formulas.

• LotusScript.

Since this document focuses on the usage of JSON in XPages we will only describe shortly how well

‘traditional’ design elements can be used to produce Notes data as JSON.

ViewColumn

Put the following piece of code (@Formula) into a view column; in _exclude list all items from the

document that should not be included in the output. All items in the document that are not excluded are

used to build a JSON String.

_exclude:="$FILE":"$Fonts":"Form":"$UpdatedBy":"$Revisions":"ID":"ModifiedBy":"AddressInvoiceAppartment";

_fld:=@Trim(@ReplaceSubstring(@DocFields;_exclude;@Nothing));

"{\"@unid\":\""

+@Text(@DocumentUniqueID)+"\","

+ @Implode (

@Transform (

_fld; "_fn" ; "\"" + _fn + "\":\"" + @Text ( @GetField ( _fn) ) + "\"" ) ; "," ) +

"},"

You can retrieve the JSON string e.g. by creating a “XAgent” (see ‘Example – XAgent collects column

values’).

Fields

A JSON object can be stored in a Notes data field as a string.

A use case could be that you have a products catalog with reviews and instead of storing the reviews in

separate review documents (document type = response) you store the reviews on the product document

in one single field. This can save the hassle of cleaning up the database when a product is removed from

the catalog.

To convert the string representation of the JSON data into an actual JSON object the JavaScript eval()

function can be used.

For instance if your JSON is stored on a document in a field called leaguePlayers as a string and it looks as

followed:

{

“players”: [

{“name”:”Dennis Bergkamp”,”club:Arsenal”},

{“name”:”David Beckham”,”club:Manchester United”}

]

}

You can use in the afterPageLoad event of an XPage the following JavaScript code to convert it to a JSON

object:

If (document1.hasItem(“leaguePlayers”)){

viewScope.put(“leaguePlayers”,eval(“(“ + document1.hasItem(“leaguePlayers”)[0] + “)”))

}

Now you can use this viewScope and bind it to e.g. a Repeat control or Data Table control (see examples

in the next chapter).

In your real world product catalog you probably want users to create new reviews or remove the fake

ones. In the next chapter you can find examples for this.

JSON in XPages

XPages focuses mainly on server-side programming. JSON becomes a valid data format for (XPinC) client

and web applications.

It can be created and retrieved by client and server-side code, including during full- and partial updates.

JSON can be used immediately or stored and retrieved from scope variables. It allows extended

flexibility, better responsiveness and fewer updates.

Stored JSON can contain arrays of objects used to define and control repeats.

Use of Scope Variables

In the most common cases you want to bind the JSON object (directly) to a control, e.g. repeat control or

data table control.

In case your JSON data is stored somewhere as a string, this string has to be converted to an object first

via the JavaScript eval() function. In such case the JSON object needs to be created before the page is

displayed. This can be done by using a viewScope variable and store the evaluated JSON object here. The

value of the viewScope is computed in the beforeRenderResponse event.

Example working on client side with server side data

When you want to work with a JSON object on the client side that is created with data gathered by

server side code you can use store it first in a scope variable:

var sPlayers = "{'name':'Bergkamp','country':'Netherlands'},{'name':'Zlatan','country':'Sweden'},{'name':'Beckham','country':'England'}";

applicationScope.put("AllTimeFavorites",sPlayers);

On the client side you can retrieve the value as followed:

var sAllTimeFavorites='#{javascript:applicationScope.AllTimeFavorites}';

The variable "sAllTimeFavorites" is holding the value as a string. We need to evaluate the

string/expression to convert it as a JSON object so that the client-side JavaScript can process it:

var objFavorites=eval("(["+sAllTimeFavorites+"])";

Since the eval() function is not recommended due to performance reasons, there is another way to

achieve the same result:

var objFavorites=[#{javascript:applicationScope.AllTimeFavorites}];

The only difference is that the SSJS is not enclosed inside quotes.

Example Repeat control

A common practice us to bind a Repeat control to a Scoped Variable that contains the JSON object:

Note: the viewScope ‘productOrders’ is set at the afterPageLoad event of the XPage.

The collection name (here: jDataRow) is then used within the Repeat control as reference to a specific

element in each entry via dot notation e.g.

<xp:inputText id="inputText8" value="#{jDataRow.product}"/>

Example Data Table control

Binding a Data Table control to a Scoped Variable that contains a JSON object is very similar to the

process for a Repeat control. Nevertheless follows here a description.

Note: the viewScope ‘productOrders’ is set at the afterPageLoad event of the XPage.

The collection name (here: jDataRow) is then used within the Data Table control as reference to a

specific element in each entry via dot notation e.g.

<xp:text value="#{javascript:jDataRow.product}" id="docProdName" />

Example – XAgent that collects column values

The XAgent uses a ViewNavigator for fast retrieval of the column entries. The real magic is the use of

java.lang.StringBuilder() to concat the view entries. A stringBuilder is the fastest way to concat a large

number of strings. It is faster than just to use string1 + string2.

<?xml version="1.0" encoding="UTF-8"?>

<xp:view xmlns:xp="http://www.ibm.com/xsp/core" rendered="false">

<xp:this.afterRenderResponse><![CDATA[#{javascript:try{

var externalContext = facesContext.getExternalContext();

var writer = facesContext.getResponseWriter();

var response = externalContext.getResponse();

// Set content type

response.setContentType("application/json");

response.setHeader("Cache-Control", "no-cache");

var json:java.lang.StringBuilder = new java.lang.StringBuilder();

var v:NotesView = database.getView("($jsonContact)");

//do not do AutoUpdates

v.AutoUpdate = false;

var nav:NotesViewNavigator = v.createViewNav();

nav.setEntryOptions(

NotesViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);

//enable cache for max buffering

nav.BufferMaxEntries = 400

var entry:NotesViewEntry = nav.getFirst();

while (entry != null) {

json.append( entry.getColumnValues().elementAt(0).toString());

var tmpentry:NotesViewEntry = nav.getNext(entry);

entry.recycle();

entry = tmpentry;

}

writer.write('[' + @Left(json.toString(), @Length(json.toString()) - 1) + ']');

writer.endDocument();

} catch(e){

_dump(e);

}}]]>

</xp:this.afterRenderResponse>

</xp:view>

You can enhance performance in case you fetch the column values with Java:

public class ViewColumn {

private static final String MSG_STRING_ERROR = "ERROR: ";

private static final String MSG_STRING_NOT_FOUND = " not found";

public ViewColumn() {

}

public String getViewColumnValueJSON(String viewname, int pos) {

ViewNavigator nav = null;

StringBuilder json = new StringBuilder();

json.append('[');

String strValue = "";

try {

View view = getCurrentDatabase().getView(viewname);

if (null != view) {

view.setAutoUpdate(false);

nav = view.createViewNav();

nav.setEntryOptions(ViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);

nav.setBufferMaxEntries(400);

ViewEntry entry = nav.getFirst();

while (entry != null) {

json.append( entry.getColumnValues().elementAt(pos).toString());

ViewEntry tmpentry= nav.getNext(entry);

entry.recycle();

entry = tmpentry;

}

strValue = json.toString();

strValue = strValue.substring(0, strValue.lastIndexOf(",")) + "]";

view.setAutoUpdate(true);

} else {

System.out.println(MSG_STRING_ERROR + viewname + MSG_STRING_NOT_FOUND);

}

} catch (NotesException e) {

System.out.println(MSG_STRING_ERROR);

strValue = "[{}]";

}

return strValue;

}

}

Another tip: in case you want to jump to a specific row in the ViewNavigator you can use the skip()

method e.g.

int start=50;

//try to skip 50 entries and return the entries actually skipped

int skippedEntries = viewNav.skip(start);

if (skippedEntries==start) {

//read the current entry after the skip operation

ViewEntry currEntry=viewNav.getCurrent();

//work with currEntry

//...

currEntry.recycle();

}

Use of REST Service

The Extension Library provides a REST Service control to expose REST services. The service

viewJsonService provides the content of a view in JSON format. The service viewJsonLegacyService

provides the content of a view in JSON format similar to the ?ReadViewEntries&OutputFormat=JSON

NotesView parameter.

The viewJsonService is designed to interact with front-end components (i.e. JavaScript widgets making

AJAX calls to retrieve the JSON from the service). Repeat controls are back-end controls that are typically

just bound directly to a data source or other server-side abstraction layer.

In the following description we will demonstrate the usage the viewJsonService service in combination

with the Dojo Data Grid control, another control available in the Extension Library. We will display the

‘Name’ column of the view ‘People’ in the names.nsf in the grid.

Step 1 – Set up the REST Service control

• Create an XPage and drag onto it the REST Service control (available in the Data Access section).

o In Properties All Properties \basics give the pathInfo property the value of ‘names’. This

value together with the XPage name will be the reference of the service e.g.

server/path/filename/demo.xsp/names.

o In Properties All Properties \basics\service select the xe:viewJsonService option.

� Set the following properties:

• databaseName = ‘names.nsf’.

• viewName = ‘People’.

� Add a column property (plus sign) and set the following properties:

• columnName = ‘Name’ (name of a real column in this view).

• name = Names (this will be the programmatic reference to the column

value).

• Drag a Dojo Data Grid control (available in the Dojo Layout section) on your XPage.

o In Properties All Properties \data section set the property for storeComponentId to the

id of your REST Service control e.g. restService1. This id is available in a dropdown list.

• Drag in a Dojo Data Grod Column control (same section) in the grid.

o In Properties All Properties \data section set the property for field to the programmatic

name of the column you set earlier (in this example ‘Names’).

If you preview the XPage the result should look something like this:

Note: The combination lastname/firstname separated by a comma is set by a column formula in the

view.

A (probably complete) description on using the grid control in combination with the viewJsonService has

been written by Brad Balassaitis (see recommended resources).

Below is the code of the example XPage:

<?xml version="1.0" encoding="UTF-8"?>

<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">

<xe:restService id="restService1" pathInfo="names">

<xe:this.service>

<xe:viewJsonService databaseName="names.nsf" viewName="People">

<xe:this.columns>

<xe:restViewColumn columnName="Name" name="Names"></xe:restViewColumn>

</xe:this.columns></xe:viewJsonService>

</xe:this.service>

</xe:restService>

<xe:djxDataGrid id="djxDataGrid1" storeComponentId="restService1">

<xe:djxDataGridColumn id="djxDataGridColumn1" field="Names">

</xe:djxDataGridColumn>

</xe:djxDataGrid>

</xp:view>

Example Write JSON to field

In a previous example you saw how that you can create a JSON object from a string stored in a Notes

field and bind that object to a Repeat control.

The changes are high that you want to update the content of that field too.

Let’s take our previous example with football players and their (favorite) team.

Display the values

Here a short rerun of the basics.

A field contains the JSON as a string. In the afterPageLoad event of the XPage the string is converted to

an object and stored in a viewScope variable:

If (document1.hasItem(“leaguePlayers”)){

viewScope.put(“leaguePlayers”,eval(“(“ + document1.hasItem(“leaguePlayers”)[0] + “)”))

}

A Repeat control uses this viewScope as data-binding source. The Repeat control contains 2 fields to

display the fields of the JSON object and a button to remove a single object.

Here is what our XPage looks like. Note it contains two other fields, to store some data for the Notes

document.

The code looks like as followed:

<xp:table border="0" style="width:100.0%;margin-left:-240px">

<xp:tr>

<xp:td style="width:100px"><xp:label value="Competition:" id="Competition"></xp:label></xp:td>

<xp:td><xp:inputText id="inputText1" value="#{document1.leagueName}" style="width:200px"></xp:inputText></xp:td>

</xp:tr>

<xp:tr>

<xp:td>

<xp:label value="Description:" id="label4"></xp:label></xp:td>

<xp:td><xp:inputTextarea id="inputTextarea1" value="#{document1.leagueDescription}"

style="width:200px;height:60px"></xp:inputTextarea></xp:td>

</xp:tr>

<xp:tr>

<xp:td></xp:td>

<xp:td>

<xp:panel>

<xp:table>

<xp:tr>

<xp:td style="width: 200px"><xp:label value="Name:" id="label1" style="font-weight:bold"></xp:label></xp:td>

<xp:td style="width: 200px"><xp:label value="Club:" id="label2" style="font-weight:bold"></xp:label></xp:td>

</xp:tr>

<xp:repeat id="repeat1" rows="30" value="#{viewScope.leaguePlayers}" var="data" indexVar="index">

<xp:tr>

<xp:td style="width: 200px"><xp:text escape="true" id="computedField2" value="#{data.name}"></xp:text></xp:td>

<xp:td style="width: 200px"><xp:text escape="true" value="#{data.club}" id="computedField1"></xp:text></xp:td>

</xp:tr>

</xp:repeat>

</xp:table>

</xp:panel>

</xp:td>

</xp:tr>

</xp:table>

Edit the values

In order to edit the values we change the Computed Field controls to Edit Box controls and we add a Link

control to remove a single object in the JSON object. Of course you want to save your changes

ultimately.

Edit Box control: <xp:inputText id="inputText8" value="#{data.name}" style="width: 200px"></xp:inputText>

Save Button control

This Button control has 2 actions:

• Get the values from the viewScope variable, convert the object to a string and sets the value in a

Notes field.

• Save the Notes document.

<xp:button value="Save" id="button1">

<xp:eventHandler event="onclick" submit="true" refreshMode="complete" immediate="false" save="false">

<xp:this.action>

<xp:actionGroup>

<xp:executeScript>

<xp:this.script><![CDATA[#{javascript:var tmpPlayers = viewScope.get("leaguePlayers");

if (tmpPlayers != null){

var stringValue = "[";

for(var a=0;a<tmpPlayers.length;a++){

stringValue += "{";

for(var i in tmpPlayers[a]){

if(tmpPlayers[a][i] == null){tmpPlayers[a][i]=""};

stringValue += "\"" + i + "\":\"" + tmpPlayers[a][i] + "\",";

}

stringValue = stringValue.slice(0,-1);

stringValue += "},";

}

stringValue = stringValue.slice(0,-1);

stringValue += "]";

document1.setValue("leaguePlayers",stringValue);

}}]]></xp:this.script>

</xp:executeScript>

<xp:saveDocument var="document1"></xp:saveDocument>

</xp:actionGroup>

</xp:this.action>

</xp:eventHandler>

</xp:button>

Remove Link control

The remove Link control removes a single object, corresponding to the index name (index) of the Repeat

control, from the object in the viewScope variable. The splice() method is used for this.

var Players = viewScope.get("leaguePlayers");

if (Players.length > 1) {

//Create new temporary object from the object but without selected item

var tmpPlayers = Players.splice(index,1);

var tmpPlayersLength = tmpPlayers.length;

viewScope.put("tmpPlayersLength", tmpPlayersLength);

//Create new string from temporary object

var stringValue = "[";

for (var a = 0; a < tmpPlayers.length; a++){

stringValue += "{";

for (var i in tmpPlayers[a]) {

if (tmpPlayers[a][i] == null) {

tmpPlayers[a][i] = "";

};

stringValue += "\"" + i + "\":\"" + tmpPlayers[a][i] + "\",";

}

stringValue = stringValue.slice(0,-1);

stringValue += "},";

}

stringValue = stringValue.slice(0, -1);

stringValue += "]";

//Create a new object from the string

var objPlayers = eval("(" + stringValue + ")");

viewScope.put("leaguePlayers",objPlayers);

} else {

viewScope.put("leaguePlayers",eval("(\[\{\"name\":\"\" , \"club\":\"\"\}\]\)"));

}

Add Player Button control

In order to store more than 1 object in the JSON object we need to add a control. Basically the control

will add a new object to the JSON object in the viewScope and give it empty values by default.

var Players = viewScope.get("leaguePlayers");

if (Players != null){

//Create string from current object

var stringValue = "[";

for(var a=0;a<Players.length;a++){

stringValue += "{";

for(var i in Players[a]){

if(Players[a][i] == null){Players[a][i]=""};

stringValue += "\"" + i + "\":\"" + Players[a][i] + "\",";

}

stringValue = stringValue.slice(0,-1);

stringValue += "},";

}

//Add a new object with empty values as a string

stringValue += "{\"name\":\"\" , \"club\":\"\"\}\]";

//Create new object via eval() function

var objPlayers = eval("(" + stringValue + ")");

viewScope.put("leaguePlayers",objPlayers);

}else{

viewScope.put("leaguePlayers", eval("(\[\{\"name\":\"\" , \"club\":\"\"\}\]\)"))

}

Result

Below you can see how the result looks like:

Recommended resources

Below you can find an overview of recommended sources of information.

Title Author

JMP303 JSON in client- and server-side code

Master Class (Lotusphere 2011) (link)

Scott Good

Use @Transform to build JSON and consume the

output in an XAgent (link)

Ulrich Krause

Use JSON to Create Flexible, Scalable, and Easy-

to-Use Data Storage in XPages (link)

Kathy Brown

Dojo Data Grids (link) Brad Balassaitis