Step 3 - Returning a JSON Customer List from the REST Service
Now that we have a working REST service, lets add a new JSON endpoint. To do this we have to add a new method. Let's start with a test request to return a list of customers.
Data can be returned in a number of different ways:
- Simple values (strings, dates, numbers, booleans, binary data)
- Objects including complex objects w/ child objects
- Collections
- Cursors
For more detailed info on what each of these results looks like check out the various Json serialization formats.
Return Cursor Data
Since we're talking about FoxPro here and the most common way to create a 'list' is to create a cursor from a query, the first examples demonstrate how to return a cursor through the REST service. To do this add the following method to the CustomerRestService
class:
************************************************************************
* Customers
****************************************
FUNCTION Customers()
SELECT id,firstname,lastname,company,entered FROM Customers ;
ORDER BY Company ;
INTO CURSOR TCustomers
RETURN "cursor:TCustomers"
or if you're using the business object from the Business Object Tutorial instead:
************************************************************************
* Customers
****************************************
FUNCTION Customers()
loCustBus = CREATEOBJECT("cCustomer")
*** Business object loads TCustomers cursor
lnCount = loCustBus.GetCustomers()
IF loCustBus.lError
ERROR loCustBus.cErrorMsg
ENDIF
RETURN "cursor:TCustomers"
Note that the syntax used to return a FoxPro cursor or table is a string "cursor:AliasName"
, which is a special serialization format. When the built in JSON parser sees this format for the result it then serializes the FoxPro cursor into JSON.
To hit this request in the browser:
http://localhost/customerdemo/CustomerList.csvc
The JSON result from this request looks like this:
[
{
"id": "_4FG12Y7U6",
"company": "Avionics Inc.",
"firstname": "John",
"lastname": "Doe",
"entered": "2013-06-12T10:00:00Z"
},
{
"id": "_4FG12Y7TX",
"company": "Avis World Headquarters",
"firstname": "Steve",
"lastname": "Herbin",
"entered": "2013-09-10T10:00:00Z"
},
{
"id": "_4FG12Y7TV",
"company": "BGP Productions",
"firstname": "German",
"lastname": "Vicencio",
"entered": "2013-09-30T12:46:40Z"
}
... additional data omitted
]
The result is a JSON array of objects with each field mapped to a property of each embedded object.
Returning Collections
Cursors are one way to return lists, but you can also return list data as collections. wwJsonSerializer supports native FoxPro Collection
objects or Web Connection wwCollection
and wwNameValueCollection
objects for list serialization. Collections are serialized as JSON arrays, but FoxPro array results are not supported because you can't return an array from a method in FoxPro. You can however return an object that has an array property and the nested one-dimensional array will serialize properly
The following code is not really practical but it demonstrates how you can return a list that is a list of objects for our customer list:
************************************************************************
* Customers
****************************************
FUNCTION Customers()
loCustBus = CREATEOBJECT("cCustomer")
*** Business object loads TCustomers cursor
lnCount = loCustBus.GetCustomers()
IF loCustBus.lError
ERROR loCustBus.cErrorMsg
ENDIF
*** Turn the cursor into FoxPro collection
loCustomers = CursorToCollection("TCustomers")
RETURN loCustomers
The JSON result for this is actually identical to the result shown above because in this case, because the collection contains objects that match the fields in the table.
Capitalization
If you look closely at the returned results you'll notice that all property/field names are returned in lower case. This is because FoxPro doesn't give you proper names when using the built in class and database Reflection APIs (everything is returned in upper case). By default wwJsonSerializer serializes all object properties and field names in lower case.
You can however override this behavior using the wwJsonSerializer::PropertyNameOverrides property. This property allows to specify a comma delimited list of properties and their properly cased names. The serializer then replaces the generated names with the ones you specify.
To do this you hook Serializer::PropertyNameOverrides
to override the values:
************************************************************************
* Customers
****************************************
FUNCTION Customers()
SELECT * FROM Customers ;
ORDER BY Company ;
INTO CURSOR TCustomers
*** Override any property names with special case
JsonSerializer.PropertyNameOverrides = "firstName,lastName"
RETURN "cursor:TCustomers"
JsonSerializer
and also JsonService
are PRIVATE
variables that are defined by wwJsonService
and are passed down to your service method via the call stack. They're in scope inside of any REST process method and allow you to customize the behavior of the serializer and service implementation.
Here we override property names and as you can see I only override the properties that I know need to change - namely the onces that require camelCase which are the 'multi-part' names like firstName
and lastName
. When the JSON is renderered it now looks like this:
[
{
"company": "Avionics Inc.",
"entered": "2013-06-12T10:00:00Z",
"firstName": "John",
"id": "_4FG12Y7U6",
"lastName": "Doe"
},
{
"company": "Avis World Headquarters",
"entered": "2013-09-10T10:00:00Z",
"firstName": "Steve",
"id": "_4FG12Y7TX",
"lastName": "Herbin"
},
...
]
and you can see lastName
and firstName
properly rendered in the list.
JavaScript Naming Conventions
JavaScript objects returned from services generally use property names that are camel cases, which means they start with lower case letters for the first phrase, and capitalized second phrase. So the
FirstName
table field in the example above becomesfirstName
using camel case. This is a convention and not a requirement, but when you design an API for public consumption try to follow these rules. By usingPropertyNameOverrides
in your API you can easily create properly formatted properties and in most cases there tend to be only a few fields that need custom formatting.
© West Wind Technologies, 1996-2024 • Updated: 01/31/16
Comment or report problem with topic