Showing posts with label Webservice. Show all posts
Showing posts with label Webservice. Show all posts

Sunday, August 2, 2020

PeopleSoft Reporting Web Services

While browsing through Peoplebooks for API syntax I ran into this topic related to Reporting web services and I was pleasantly surprised that this feature is available out of the box but I don't think is used frequently. Looks like it has been around since 8.56.x  or maybe even before. So PeopleSoft provides SOAP as well as REST based web services to expose PeopleSoft data based on PS Query or Connected query to external systems. I think this is pretty cool. So as long as the data set can be constructed in PS Query or Connected Query it can be exposed out to a 3rd party service using out of the box infrastructure. This greatly simplifies elaborate design and development time of application engine programs which are generally used for building interfaces.

PeopleSoft provides QAS_QRY_SERVICE which is the service for Query Access Manager.
and PROCESSREQUEST which is the service for Process Scheduler. Via these services once can create and get query and process request type items. I tried out the query service to execute a query via Postman and it works like a charm.

There is lot of good info in PeopleBooks so I won't repeat all of that here. So I currently have two RSS feeds based on PS Query which I use for monitoring the status of process monitor processes and jobs and another one which monitors the asynchronous  services. I used these as the test cases to fetch data using the web services instead of RSS. Also setup a user which has access to the service operation and to the queries and can only run the queries. 

As I have the queries already built I tested out the REST service operation QAS_EXECUTEQRY_REST_GET to execute the queries. 

Example of the URI Template:
{OwnerType}/{QueryName}/{OutResultType}/{OutResultFormat}?isconnectedquery=
{isConnectedQuery}&maxrows={MaxRow}&prompt_psqueryname={Prompt_PSQueryName*}
&prompt_uniquepromptname={Prompt_UniquePromptName*}&prompt_fieldvalue=
{Prompt_FieldValue*}&filterfields={FilterFields*}&json_resp={json_response}
Provided QueryName, OwnerName is PUBLIC as the query I am using is public query, OutResultType is JSON as I want the results back in JSON format, OutResultFormat is NONFILE as I want the response in message object, isconnectedquery = N, maxrows set it to 100 ( I would never get so many rows back), json_resp set it to true, did not set anything for rest of the URI parameters. 

HTTP GET https://myserver/PSIGW/RESTListeningConnector/PSFT_HR/ExecuteQuery.v1/PUBLIC/N_MY_QUERY/JSON/NONFILE?isconnectedquery=N&maxrows=100&prompt_psqueryname=&prompt_uniquepromptname=&prompt_fieldvalue=&filterfields=&json_resp=true 


The response is received as follows:

{
   "status":"success",
   "data":{
      "query":{
         "numrows":3,
         "queryname=":"N_MY_QUERY",
         "rows":[
            {
               "attr:rownumber":1,
               "PRCSINSTANCE":558373,
               "MAINJOBINSTANCE":558373,
               "PRCSJOBNAME":"MYJOB",
               "PRCSTYPE":"PSJob",
               "PRCSNAME":"MYPRCS",
               "OPRID":"PS",
               "RUNCNTLID":"MYRUN",
               "RECURNAME":"Daily",
               "RUNDTTM":"2020-07-04T12:15:00-0400",
               "RUNSTATUS":"Processing",
               "DISTSTATUS":"Scheduled"
            },
            {
               "attr:rownumber":2,
               "PRCSINSTANCE":558374,
               "MAINJOBINSTANCE":558373,
               "PRCSJOBNAME":"MYJOB",
               "PRCSTYPE":"SQR Process",
               "PRCSNAME":"MYSQRPRCS",
               "OPRID":"PS",
               "RUNCNTLID":"MYRUN",
               "RECURNAME":"Daily",
               "RUNDTTM":"2020-07-04T12:15:00-0400",
               "RUNSTATUS":"Processing",
               "DISTSTATUS":"Scheduled"
            },
            {
               "attr:rownumber":3,
               "PRCSINSTANCE":558375,
               "MAINJOBINSTANCE":558373,
               "PRCSJOBNAME":"MYJOB",
               "PRCSTYPE":"SQR Report",
               "PRCSNAME":"MYPROCESS",
               "OPRID":"PS",
               "RUNCNTLID":"MYRUN",
               "RECURNAME":"Daily",
               "RUNDTTM":"2020-07-04T12:15:00-0400",
               "RUNSTATUS":"Pending",
               "DISTSTATUS":"Scheduled"
            }
         ]
      }
   }
}


Following is an example when query does not return any results.

{
   "status":"success",
   "data":{
      "query":{
         "numrows":0,
         "queryname=":"N_MY_QUERY",
         "rows":[

         ]
      }
   }
}

Tuesday, April 21, 2020

Creating JSON request for REST web services

PeopleSoft provides Document technology to be used to generate JSON request messages but in my experience they are very restrictive especially when working on integrating with 3rd party web services. So following is what I did to generate a JSON request message to post to a 3rd party REST web service. 

The request that I have to generate is in the following form.
[
   {
      "attrib1":"value1",
      "attrib2":"value2",
      "attrib3":{
         "attrib3_1":"values3_1",
         "attrib3_2":"values3_2",
         "attrib3_3":"values3_3",
         "attrib3_4":"values3_4"
      }
   }
]

I am running PT 8.57.x and at this time its not possible to build a document with the root node as an array as shown in the example below. Also I have nested compounds which is also a challenge, the parent compound does not have a label where as the child does. 
So to build something like above I am using the CreateJsonBuilder API provided by PeopleSoft.

Local JsonBuilder &jbldr = CreateJsonBuilder();
Local JsonArray &jArray;
Local string &json;
Local message &request, &response;
Local boolean &bRet;

&jbldr.StartArray(""); /* no label */
 &jbldr.StartObject(""); /* no label */
  &jbldr.AddProperty("attrib1", "value1");
  &jbldr.AddProperty("attrib2", "value2");
   &jbldr.StartObject("attrib3"); /* need a label */
    &jbldr.AddProperty("attrib3_1", "value3_1");
    &jbldr.AddProperty("attrib3_2", "value3_2");
    &jbldr.AddProperty("attrib3_3", "value3_3");
    &jbldr.AddProperty("attrib3_4", "value3_4");
   &jbldr.EndObject("attrib3"); /* closing out the compound or JSONObject */
 &jbldr.EndObject("");
&jbldr.EndArray("");


/* this will return the array just like what I want */
&jArray = &jbldr.GetRootNode().GetJsonObject().GetJsonArray("");
&json = &jArray.ToString();

Created a basic non-rowset based message and assigned that as the request message in my service operation. Use this method to set the content for the message segment for a non-rowset-based message only.

&bRet = &request.SetContentString(&json);
&response = %IntBroker.SyncRequest(&request);

That's it, works like a charm.

Sunday, March 8, 2020

Event Mapping (managing customizations)

Event Mapping is a PeopleSoft delivered framework that helps applications and customers adapt PeopleSoft to meet their business needs with minimum upgrade impact. It introduces business logic as a configuration instead of customization. Event mapping allows the user to develop reusable code and artifacts and ensures that the customer’s code will not be overwritten by PeopleSoft delivered code during an upgrade.

Following is what I have tried in PeopleTools 8.56.07 and 8.57.08 on HCM 9.2 (PUM 31 and beyond). For this POC I am going to call my custom application package peoplecode on SavePostChange event on the DEPARTMENT_TBL component to perform something like sending out a notification. In the absence of the event mapping functionality I would have written this code in SavePostChange event of the DEPARTMENT_TBL component thus customizing the delivered object.

So, step one is to build the application package peoplecode. Import of PT_RCF:ServiceInterface is the key. The class name can be anything but the method name has to be "Execute".

import PT_RCF:ServiceInterface;

class FoundationDataNotify extends PT_RCF:ServiceInterface
   method Execute();
end-class;

method Execute
   /+ Extends/implements PT_RCF:ServiceInterface.execute +/

   /* custom logic to send out notification goes here */
end-method;
  
Once the package is developed navigate to PeopleTools > Portal > Related Content Service > Define Related Content Service. Create a new definition and select "URL Type" as application class and then provide the package details. For this POC I selected "Public Access" under security options. Save the service definition.

Then navigate to PeopleTools > Portal > Related Content Service > Manage Related Content Service, Event Mapping tab. Click on the "Map Application Classes to Component Events" link and then navigate the portal tree to find the component on which this event would be based on or associated with. In my case it would be the department table component located under Set Up HCM > Foundation Tables > Organization > Departments.

Under "Component Level Event Mapping" section, select "enable" check-box, event name would be SavePostChange and select the Service ID created in the earlier step. Set sequence number to 1 and processing sequence as "Post Process". Save the page and that is it. 

Navigate to the department component make a change and save the page and verify that the custom application package peoplecode fires.


The PeopleCode Editor provides an Event Mapping button in the dynamic Application Designer toolbar. For an application developer, the Event Mapping button is a visual indicator of custom PeopleCode programs mapped to events of a component, component page, component record, or component record field. This button is not available for page level event mapping.







So opened component peoplecode of component DEPARTMENT_TBL and clicked on the button to produce the following output.
















Right-clicking on the application package code and selecting "Event Mapping References" in the pop-up menu as shown below should display the component but that did not work.













"Event Mapping References" tab is empty as shown below.












So I guess this might work in future releases or maybe I don't have something configured correctly for this to work.

Wednesday, April 4, 2018

PeopleSoft web service response namespace issue

Running PT 8.53.x, wrote a simple custom web service "Hello World". Service works fine but saw one issue where the namespace returned by the response message is not defined in the WSDL. So any other 3rd party solution like a .Net program when they consume and execute this service it fails to receive the response due to the unidentified namespace.

Request 


<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing/" xmlns:xsd="http://www.w3.org/2001/XMLSchema/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance/">

  <soapenv:Header xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  </soapenv:Header>
  <soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <HelloWorld xmlns="http://xmlns.oracle.com/Enterprise/Tools/schemas/HELLOWORLD.V1"></HelloWorld>
  </soapenv:Body>
</soapenv:Envelope>

Response via PeopleSoft SOAP message template


<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://schemas.xmlsoap.org/ws/2003/03/addressing/" xmlns:xsd="http://www.w3.org/2001/XMLSchema/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance/">
  <soapenv:Header xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  </soapenv:Header>
  <soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <HelloWorldResponse xmlns="http://xmlns.oracle.com/Enterprise/Tools/schemas/HELLOWORLDRESPONSE.V1">
      <HelloWorldResult>XYZ</HelloWorldResult>
    </HelloWorldResponse>
  </soapenv:Body>
</soapenv:Envelope>

Actual response via SOAPUI or Postman

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Body>
      <HelloWorldResponse xmlns="http://peoplesoft.com/HelloWorldResponseResponse">
         <HelloWorldResult>Success</HelloWorldResult>
      </HelloWorldResponse>
   </soapenv:Body>
</soapenv:Envelope>


As you see the the problem is the namespace value returned for the root tag "HelloWorldResponse".

<HelloWorldResponse xmlns="http://peoplesoft.com/HelloWorldResponseResponse">


PeopleSoft puts the default value (not sure where this is stored)
http://peoplesoft.com/HelloWorldResponseResponse
instead of what is defined in the WSDL which is 
http://xmlns.oracle.com/Enterprise/Tools/schemas/HELLOWORLDRESPONSE.V1

I have a very basic synchronous service operation, with a request and response message and the handler OnRequest peoplecode is doing all the work.

Following was my application package peoplecode initially.

&response = CreateMessage(Operation.HELLOWORLD_SVCOP, %IntBroker_Response);
&xmldata = "<?xml version='1.0'?><HelloWorldResponse/>";
&xmlresponsedoc = CreateXmlDoc(&xmldata);

&HWResponseNode = &xmlNode.AddElement("HelloWorldResult");
&HWResponseNode.NodeValue = "Success";
&response.SetXmlDoc(&xmlresponsedoc);
Return &response;


So when creating the XmlDoc I was not specifying any namespace and I was hoping PeopleSoft will add it based on the schema definition. 

Instead following is what I had to do to correct the issue. (only displaying the change, no change to rest of the code)

&xmlresponsedoc = CreateXmlDoc("");

&docTypeNode = &xmlresponsedoc.CreateDocumentType("", "", "");
&rootNode = &xmlresponsedoc.CreateDocumentElement("HelloWorldResponse", "http://xmlns.oracle.com/Enterprise/Tools/schemas/HELLOWORLDRESSPONSE.V1", &docTypeNode);

&xmlNode = &xmlresponsedoc.DocumentElement;

So I have to use the CreateDocumentElement method of CreateXmlDoc in order to define the namespace. In order to use this method I had to first initialize the &docTypeNode variable by using the CreateDocumentType method.

After making the above change the response comes back correctly.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Body>
      <HelloWorldResponse xmlns="http://xmlns.oracle.com/Enterprise/Tools/schemas/HELLOWORLDRESSPONSE.V1">
         <HelloWorldResult>Success</HelloWorldResult>
      </HelloWorldResponse>
   </soapenv:Body>
</soapenv:Envelope>

Saturday, March 26, 2016

Sending and Receiving MTOM-encoded binary data

PeopleSoft supports the MTOM protocol for sending and receiving binary data using service operations. While you can send and receive binary data using SOAP, doing so requires that you Base64-encode the data, which can increase message size by 33 percent or more. The MTOM protocol enables you to send and receive binary data in its original binary form, without any increase in size due to encoding.
For sending or receiving MTOM-encoded binary data, we have to use message segments to store the data. The SegmentContentType property of the message object is used to set or read the content type of each message segment.

Following is a test that I did to send a XML file as an attachment in a SOAP message and then read the attachment that is sent by the 3rd party system that I am interacting with. I am running PT 8.53.22.

Sending:

Request message is as shown below. For this test I am storing this in a html object called as MY_MESSAGE but this can be generated dynamically as needed using SOAPDoc or XMLDoc classes. The request message defined on the service operation is nonrowset based. 

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<submitLargeDocument>        
    <Document>  
         <PsftXopInclude SegmentNumber='1'/> 
    </Document>      
</submitLargeDocument>
</soapenv:Body>
</soapenv:Envelope>

document.xml is my payload which I have created in a different routine and I am just using it here. Its just like creating any other XML file in PeopleSoft. Payload can be a binary file like a pdf or a image file.
Sender node is my default local node and receiving node is the delivered WSDL_NODE. You can always create a custom receiving node if needed. Routing is using local gateway and HTTPTARGET connector. Under routing, connector properties content-type is set to text/xml as my payload is a xml file, HTTPPROPERTY MTOM is set to Y, METHOD is POST, SOAPUpContent is set to N as I have already built the SOAP wrapper in my html object above. If you need IB to create the wrapper then set this property to Y. Provided PRIMARYURL to destination 3rd party application. Took all other defaults.

PeopleCode:

&str = GetHTMLText(HTML.MY_MESSAGE);
&requestXMLDoc = CreateXmlDoc();
&ret = &requestXMLDoc.ParseXmlString(&str);

&request = CreateMessage(Operation.SEND_OPERATION);
&request.SetXmlDoc(&requestXMLDoc);

&MTOMFile = GetFile("C:\temp\document.xml", "R", %FilePath_Absolute);
If &MTOMFile.IsOpen Then   
   &theBase64encodedString = &MTOMFile.GetBase64StringFromBinary();   
   &MTOMFile.Close();
End-If;

&request.CreateNextSegment();

If (&request.SetContentString(&theBase64encodedString)) Then   
    &request.SegmentContentType = "application/xml";  
    &request.SegmentContentTransfer = %ContentTransfer_Binary;
End-If;

&response = %IntBroker.SyncRequest(&request);

Receiving:

Response message defined on the service operation is a non-rowset based message. Sender node is the default local node and receiving node is WSDL_NODE. Using local gateway and HTTPTARGET connector. Setting HEADER properties Content-Type to text/xml as the response attachment that I am receiving is a xml file, sendUncompressed is Y, HTTPPROPERTY Method is POST and SOAPUpContent is Y and finally the PRIMARYURL to the 3rd party service.

On the weblogic webserver, in the integrationGateway.properties file enable the MTOM Listening Connectors. 
ig.MTOM.enablePeopleSoftServiceListeningConnector=true
ig.MTOM.enableHttpListeningConnector=true

Bounce the webserver after making this change.

PeopleCode:

This is pretty straightforward. Once the request is made, read the response and parse out the document.

&response = %IntBroker.SyncRequest(&request);
&responseXMLDoc = &response.GetXMLDoc();

If (&response.ResponseStatus = 0) Then
     &dataNode = &responseXMLDoc.DocumentElement.GetElementByTagName("data");
     &theData = &dataNode [1].GetCDataValues();
     &responsestr = &theData.Shift();
End-If;

The &responsestr string variable will have the response SOAP envelope as well as the attachment separated by message segments as shown below. Parsed it out using string functions.

<?xml version="1.0"?>
<data psnonxml="Yes">
  <![CDATA[
------=_Part_624_1792156364.1458048147094
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: 8bit
Content-ID: <soap.xml@xfire.codehaus.org>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
.......
.......
.......
</soap:Body>
</soap:Envelope>
------=_Part_624_1792156364.1458048147094--

------=_Part_724_1792156364.1458048147094
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: 8bit
Content-ID: <soap.xml@xfire.codehaus.org>

<Document>
........
........
........
</Document>

------=_Part_724_1792156364.1458048147094--

]]>
</data>


Monday, November 23, 2015

PeopleSoft RESTful Webservice

Here is my initial exposure to creating a RESTful webservice in PeopleSoft. I am running HR 9.1 on PT 8.53.x. 
The requirement is to provide a list of locations based on a region input and then provide a list of employees in that region/location combination.
Here is the flow.


This is accomplished by creating a provider service using the GET method.
I am assuming the basic IB setup is in place, which is essentially making sure the local gateway is up and running and under Service configuration, the REST target location is provided. 

Navigate to PeopleTools > Documents > Document Builder.
First create a document with 2 primitives called as "REGION" and  "LOCATION".
We could have just created a non-rowset based message, but document object provides the flexibility to exchange data in XML or JSON format whereas non-rowset based message would be just XML.
Both primitives will be of the type string, sub-type = None. Adjust the length appropriately, in my case region is just a 1 char code and location is 10 chars.

Now create a response document. This document will have 2 collections one for the location data and the other for the employee data. Under each collection I added a compound child which is essentially a view (record) that I created which will return the appropriate data elements. 
LOC_RESPONSE  -- root element
      LOC_DATA -- collection
            LOC_DTL_VW -- compound
      EMP_DATA -- collection 
            EE_LIST_VW -- compound 

When the compound child is created behind the scenes PeopleSoft creates another document. So make sure the naming of each type is appropriate otherwise it can get pretty confusing. 

Now I created another document which I am going to use for handling fault, this has only one primitive in it of the type text.

After each document is created and saved, verify the document using the "Validate" and "Document Tester" options available on the page.

From the document now create messages, so navigate to PeopleTools > Integration Broker > Integration Setup > Messages. Add new messages of type document. You can keep the document name and message name same. Did this for all the three documents that I created in the earlier step. Once in just click save. The page looks similar to the document builder page, with the addition of the metadata reference section visible at the top of the page. No additional work is required here.

Now navigate to PeopleTools > Integration Broker > Integration Setup > Services and create a REST service. Make sure the "REST Service Type" check-box and the "Is Provider" check-box is checked. Save the service. 
Now select the REST method, in my case "Get", provide a service operation name and click on Add. The REST method is appended to the service operation name.

the REST Base URL will be grayed out and would be auto-populated with the setup info and service name. Under the URI section I have the following indexes.

1. Locations/?region={REGION}
2. Locations/{REGION}/?location={LOCATION}

Document Template - lookup the request document that was created in the first step above. This is the document which has the 2 primitives defined. The primitive name should match what is provided in the curly brackets under the URI section. 

Make the service operation active. Generate any-to-local routing.
Associate the response and fault messages appropriately. In my case the data exchange would be done in JSON format, so under content type I selected "application/json".

Provide service operation security. Verify routing info. Enable logging if required. Doesn't help much in case of JSON messages though.

For the handler section we need to write application package peoplecode for the OnRequest method.  
Following is the basic structure.


import PS_PT:Integration:IRequestHandler;

class Locations implements PS_PT:Integration:IRequestHandler
   method Locations();
   method OnRequest(&MSG As Message) Returns Message;
   method OnError(&request As Message) Returns string;
end-class;

/* constructor */
method Locations
end-method;

method OnRequest
   /+ &MSG as Message +/
   /+ Returns Message +/
   /+ Extends/implements PS_PT:Integration:IRequestHandler.OnRequest +/
   /* Variable Declaration */
  
End-method;

method OnError
   /+ &request as Message +/
   /+ Returns String +/
   /+ Extends/implements PS_PT:Integration:IRequestHandler.OnError +/


End-method;


Once the service operation is saved, a WADL has to be generated. So navigate back to services and click on the "Provide Web Service" link and step through the wizard.

Once this is done you can test it from an utility like SOAPUI (yes can be used to test RESTful web services) or plain browser or via PeopleTools > Integration Broker > Services Utilities > Provider REST Template.

So the call to this service would be like so.
http://servername/PSIGW/RESTListeningConnector/Locations.v1/Locations/?region=A
which will return all locations in region A and,


http://servername/PSIGW/RESTListeningConnector/Locations.v1/Locations/A/?location=XYZ01
which will return employees in region A and Location XYZ01. Location XYZ01 is returned by the first call.

Output 1:

<?xml version="1.0"?> 
<data psnonxml="Yes"> 
  <![CDATA[
{"LOC_RESPONSE": {
"LOC_DATA": [
{
"ADDRESS1": "1 Palace Dr",
"CITY": "",
"DESCR": "My Location",
"DESCRSHORT": "My Location",
"EFFDT": "2010-08-02",
"EFF_STATUS": "A",
"EXTENSION": "",
"FAX": "",
"LOCATION": "XYZ01",
"MESSAGE_TEXT2": "",
"PHONE": "",
"POSTAL": "",
"STATE": ""
},
{
"ADDRESS1": "100 Main Street",
"CITY": "",
"DESCR": "New Location",
"DESCRSHORT": "New Location",
"EFFDT": "2011-08-02",
"EFF_STATUS": "A",
"EXTENSION": "",
"FAX": "",
"LOCATION": "ABC01",
"MESSAGE_TEXT2": "",
"PHONE": "",
"POSTAL": "",
"STATE": ""
}
],"EMP_DATA": {"DEPTNAME": "","EMAIL_ADDR": "","NAME": "","PHONE": "","TITLE": ""}}
} ]]> 
</data>

Output 2:

<?xml version="1.0"?> 
<data psnonxml="Yes"> 
  <![CDATA[{"LOC_RESPONSE": {
"EMP_DATA": [
{
    "DEPTNAME": "My Location Dept",
"EMAIL_ADDR": "jon.doe@company.com",
"NAME": "Doe,Jon",
"PHONE": "123456789",
"TITLE": "Managing Director"
},
{
    "DEPTNAME": "My Location Dept",
"EMAIL_ADDR": "jane.doe@company.com",
"NAME": "Doe,Jane",
"PHONE": "123456789",
"TITLE": "Managing Director"
}
]}
} ]]> 

</data>