Skip to Content

$Expand is supported in soap data source when a web service has an operation which returns 2 structures and there has to be a primary key, foreign key relationship between them. There has to be provision to create a navigation property between these 2 entities in odata model

Steps from Design Time tool

  1. Create an OData Service Implementation Project
  2. Create an OData Model for 2 entities Header and Items.
    Define navigation from Header to Items as below. This model fetches collection of header and the items associated with that header. So the web service must have an operation which can return collection of header and collection of items. PROP1 is a primary key in ZKRAN_HDR entity set and PROP1 is a foreign key in ZKRAN_ITEM entity set. In the association defined PROP1 of header entity will be a principal key and PROP1 of item entity will be a dependent key.

image1.PNG

Image8.PNG

   3. Right click on odatasvc and choose Select data source

   4. Select the HDR entitySet and choose the read CRUD operation. Select the data source as SOAP Service. Note: query operation can also be done

Image2.PNG

  5. Specify the wsdl file and choose the correct operation from the list of soap operations and click on finish.

Image3.PNG

   6. Right click on Query and select Define Custom Code

Image4.png

   7. Select the script type as either javascript or groovy script

Image5.PNG

  8. This being a read operation do a request mapping as below

Image6.PNG

  9. Do response mapping as below which maps only properties of header entity set since data source is added for header entity. The response obtained  from web service contains the items associated with the header based on PROP1. But the items are mapped using custom code

Image7.PNG

   10. In processResponseXML function the entire response from web service is fetched. This is parsed in order to get the list of hashmaps of items. Another list of hashmap must be created whose key will be Items entity set name and value will be previously created list of hashmap



function processResponseXML(message) {
  importPackage(java.util);
  importPackage(java.lang);
  var payload = message.getBody().toString();
  var tokens = payload.split("(?=<)|(?<=>)");
  var list = new ArrayList();
  var innerMap = new LinkedHashMap();
  var items = new ArrayList();
  var foundETItems = false;
  for (var i = 0; i < tokens.length; i++) {
   if (tokens[i].contains("<ET_ITEMS>")) {
    log.logErrors(LogMessage.TechnicalError, "Found ET_ITEMS");
    foundETItems = true;
    var itemMap = new LinkedHashMap();
    // Iterate each item
    for (var j = i+1; j < tokens.length; j++) {
     if(tokens[j].contains("</ET_ITEMS>")) {
      log.logErrors(LogMessage.TechnicalError, "Completed ET_ITEMS");
      break;
     }
     if(tokens[j].contains("<item>")) {
      continue;
     }
     if(tokens[j].contains("</item>")) {
      items.add(itemMap);
      itemMap = new LinkedHashMap();
      continue;
     }
     itemMap.put(tokens[j].substring(1,tokens[j].length()-1), tokens[j+1]);
     log.logErrors(LogMessage.TechnicalError, "For entry "+tokens[j].substring(1,tokens[j].length()-1)+" added value: "+tokens[j+1]);
     j = j + 2;
    }
   }
  }
  if(foundETItems) {
   innerMap.put("ZKRAN_ITEMSet", items);
   list.add(innerMap);
  }
  // Set the list of hashMaps to a header
  message.setHeader("MappingOutput", list);
  return message;
}










  11. In processResponseData message body will have data which is the output from mapping. Data from message header and message body are combined to a new list of hashmap.


function processResponseData(message) {
importPackage(java.util);
 importPackage(com.sap.gateway.ip.core.customdev.logging);
 
 // Get the data from the previously set header
 var data = message.getHeaders().get("MappingOutput"); 
 log.logErrors(LogMessage.TechnicalError, data);
 
 var body = message.getBody();
 var newData = null;
 var newList = new ArrayList();
for (var i = 0; i < data.size(); i++) {
  newData = new LinkedHashMap();
  var dyData = data.get(i);
  var mappedData = body.get(i);
  newData.putAll(mappedData);
  newData.putAll(dyData);
  newList.add(newData);
 }
 log.logErrors(LogMessage.TechnicalError, newList);
 message.setBody(newList);
 return message;
}





Note: the entity to be expanded is fetched in processResponseData function and the entities which are associated with the main entity are fetched from processResponseXML function.

The request payload looks like

<soapenv:Body>
<urn:ZKRAN_TEST_EXPAND>      
<IV_HDR_ID>AA</IV_HDR_ID>   
</urn:ZKRAN_TEST_EXPAND>

</soapenv:Body>

The response payload looks like

<n0:ZKRAN_TEST_EXPANDResponse xmlns:n0=”urn:sap-com:document:sap:rfc:functions”>

                <ES_HDR>

                                  <PROP1>AA</PROP1>

                                  <PROP2>HelloAA</PROP2>

                                  <PROP3>BlahAA</PROP3>

                  </ES_HDR>

                  <ET_ITEMS>

                                  <item>

                                                  <PROP1>AA</PROP1>

                                                  <ITPROP2>AA1</ITPROP2>

                                                  <ITPROP3>Good Day</ITPROP3>

                                  </item>

                                  <item>

                                                  <PROP1>AA</PROP1>

                                                  <ITPROP2>AA2</ITPROP2>

                                                  <ITPROP3>Hello</ITPROP3>

                                  </item>

                  </ET_ITEMS>

  </n0:ZKRAN_TEST_EXPANDResponse>

   12. Right Click on Project and select Generate and Deploy Integration Content. This will deploy the bundle.

  Now fire an OData Request is https://localhost:8083/gateway/odata/SAP/SOAP;v=1/ZKRAN_HDRSet(‘AA’)?$expand=ZKRAN_ITEMSet on the browser and response will give collection of headers and collection of items associated with header AA.

Note: If the odata request is https://localhost:8083/gateway/odata/SAP/SOAP;v=1/Products(1)?$expand=Category,Suppliers

  1. Create a list of hashmap of attributes of category.
  2. Create a list of hashmap of attributes of Suppliers.
  3. Create a list of hashmap with key as CategoryEntitySet name and value is output from 1 and another hashmap entry with key as SupplierEntitySet name and value is output from 2.
  4. Create a list of hashmap of attributes of product and hashmap fetched from the list created in 3.

 

If Product has the below fields

        

Product_id

Product_Name

1

Pepsi

2

Amul Ice Cream

If Category has below fields

      

Category_Id

Product_id

Category_Description

10

1

Beverages

20

1

Desserts

If Supplier has below fields

    

Supplier_Id

Product_id

Supplier_Description

100

1

ABC

200

2

XYZ

$Expand output based on above mentioned steps look like

  1. {Category_Id=10,Product_id=1,Category_Description=Beverages},
    { Category_Id=20,Product_id=1,Category_Description=Desserts}
  2. {Supplier_Id=100,Product_id=1,Supplier_Description=ABC}
  3. {CategorySet=[{Category_Id=10,Product_id=1,Category_Description=Beverages},
    { Category_Id=20,Product_id=1,Category_Description=Desserts }]},
    {SupplierSet=[{Supplier_Id=100,Product_id=1,Supplier_Description=ABC}]}
  4. [{Product_id=1,Product_Name=Pepsi, CategorySet=[{Category_Id=10,Product_id=1,Category_Description=Beverages},
    { Category_Id=20,Product_id=1,Category_Description=Desserts }], SupplierSet=[{Supplier_Id=100,Product_id=1,Supplier_Description=ABC}]}]
To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Milton Chandradas

    JavaScript does not support look behind regular expressions.  Is there an equivalent of this line of code in JavaScript ?

    var tokens = payload.split(“(?=<)|(?<=>)”);

    (0) 
  2. Fenil Doshi

    Hi Ramya,

     

    Yes we are getting that error while using the Javascript for custom code, have attached the below screenshot for reference.

     

    Best Regards

    Fenil

    (0) 

Leave a Reply