Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
CarlosRoggan
Product and Topic Expert
Product and Topic Expert
For the next instance of type Edm.Blog… I’ve thought of choosing the ComplexType… Although our blog will be rather a simple type…. As usual 😉

This blog is part of our series of blogs for beginners using SAP Cloud Platform SDK for service development (see here for info)

See here for prerequisites.

In this blog we'll learn how the implementation looks like if our model contains a property of type "complexType"

 
Note:
If you don't like reading, just scroll down to the end of this page to view the full source code.

 

Intro


What is a complex type?

With my own limited English vocabulary, I can only give a simple explanation:

In my data model, I have a normal entity type which consists of several properties. That's normal.
E.g. my Person has a property which is the name of the person.
This is a normal property which has a normal simple data type, which is just a string
<Property Name="Name" Type="Edm.String"/>

Now I have a requirement for my data model: I want to store the address of the person.
No prob.
I can simply go ahead and add more normal properties to my Person entity type:
Add another property called City of type String
Add another property called Street of type String
Add another property called StreetNumber of type int
Etc
<Property Name="Name" Type="Edm.String"/>
<Property Name="City" Type="Edm.String"/>
<Property Name="Street" Type="Edm.String"/>
<Property Name="Number" Type="Edm.Int32"/>

 

But it would be better to group these new properties, because together they form the address of the person.
And this grouping is realized by the so-called complex type
In OData we can define a ComplexType and call it e.g. “Address”
Then we add properties to the ComplexType (City, Street, Number etc)
<ComplexType Name="Address">
<Property Name="City" Type="Edm.String" MaxLength="10"/>
<Property Name="Street" Type="Edm.String" />
<Property Name="Number" Type="Edm.Int32" />
</ComplexType>

Finally, in our Entity Type, we create a property called e.g. “HomeAddress” and instead of Edm.String it has the type “Address” which is the complex type defined earlier.
<EntityType Name="Person">
...
<Property Name="Name" Type="Edm.String"/>
<Property Name="HomeAddress" Type="demo.Address"/>
</EntityType>

This looks much better - don't you agree?
Note:
As you can see, the complex type has to be entered with QualifiedName , which is:
<yourNamespace>.<yourComplexTypeName>
or:
<yourAliasOfYourNamespace>.<yourComplexTypeName>

 

Does all this simple explanation help?
The professional explanation, even better: the specification can be found here

Project


How to create a project? See here.

 

Model


In this blog we’re finally enhancing our model.

We create a complex type as shown above.
Then we use it in our existing entity type "Person". As shown above.
The QualifiedName needs to be correct. So make sure to copy the namespace from where it is defined.

The namespace is defined as attribute of the schema:
<Schema Namespace="demo" xmlns="http://docs.oasis-open.org/odata/ns/edm">

 
Note:
In our example we aren't using an alias.

No more changes (for now)

Our enhanced model looks as follows:

 
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="demo" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Person">
<Key>
<PropertyRef Name="UniqueId" />
</Key>
<Property Name="UniqueId" Type="Edm.Int32" />
<Property Name="Name" Type="Edm.String"/>
<Property Name="HomeAddress" Type="demo.Address" />
</EntityType>

<ComplexType Name="Address">
<Property Name="City" Type="Edm.String"/>
<Property Name="Street" Type="Edm.String" />
<Property Name="Number" Type="Edm.Int32" />
</ComplexType>

<EntityContainer Name="container">
<EntitySet Name="People" EntityType="demo.Person" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

 

 

Code


How is the Complex Type itself realized in java code?

Yes, since it is a structured type, it is realized as a map, just like the entity type.
And yes, the entity type is a map and this map has an entry which has a value which is a (nested) map.

Confusing?

 

Does the following structural view help?

 

















































Entity Type Map
Key Value
UniqueId 1
Name Anna
HomeAddress Complex Type Map
Key Value
City Berlin
Street Hauptstrasse
Number 22


 

Or do you prefer to see it like this:
The outer map in green is for the Person (entity type), the inner map in black is for the Address (complex type)

 
















UniqueId 1
Name Anna
HomeAddress
















City Berlin
Street Hauptstr
Number 22



 

OK.

Now let’s translate this structured view into Java:

First we create the complex type map. This is the inner map which will be used later.

 
private Map<String, Object> createAddress(String cityName, String streetName, int number){

Map<String, Object> addressMap = new HashMap<String, Object>();
addressMap.put("City", cityName);
addressMap.put("Street", streetName);
addressMap.put("Number", number);

return addressMap;
}

 

Then we create the map for "Person" which has an "Address", i.e., the outer map which uses the above created map.

 
private Map<String, Object> createPerson(int id, String name, Map<String, Object> addressMap){

Map<String, Object> personMap = new HashMap<String, Object>();
personMap.put("UniqueId", id);
personMap.put("Name", name);
personMap.put("HomeAddress", addressMap);

return personMap;
}

 

You can see how the method createPerson has changed, in comparison to the previous blogs:

The value of the property “HomeAddress” is a map:
 personMap.put("HomeAddress", addressMap);

instead of a simple string.

 

CREATE with Complex Type

We’ve already learned how to implement the CREATE operation.

Just little note about the complex type:

When doing a POST request, the user has to send the payload with nested structure, due to the complex type.
In our implementation, accordingly, we get a nested structure: a map (for person) with a nested map (for address).
So we can access the outer map as usual and for the complex type we get the nested map from the value of the outer map.

 

See the full Java class at the end of this blog. As always.

 

Deploy’n’Enjoy


As always, now we can
rebuild with maven,
deploy with Cloud Foundry Client,
run with browser

 

QUERY with Complex Type

First, we want to have a look at the complex type, so we invoke the QUERY operation.

https://<yourApp>.cfapps.sap.hana.ondemand.com/odata/v4/DemoService/People



 

QUERY with System Query Options using Complex Type

These URLs show how to use $select for complex types:

 

You can pass the complex type itself to the $select

https://<yourHost>/odata/v4/DemoService/People?$select=HomeAddress

As a result, the complete complex type including all properties is displayed.

 

More interesting is to select only one property of the complex type.
The syntax is to use a slash:

https://<yourHost>/odata/v4/DemoService/People?$select=HomeAddress/City

As a result, only the cities are displayed, nothing else

 

The following URL shows how to combine normal properties with nested properties

https://<yourHost>/odata/v4/DemoService/People?$select=Name,HomeAddress/City

 

Here's an extra box, such that you better remember the syntax for
$select with <normalProp> and <propOfComplexType>
$select=Name,HomeAddress/City

 

CREATE with Complex Type

In order to send a valid POST request, the request payload should look like this

 
{
"UniqueId": 4,
"Name": "Eric",
"HomeAddress": {
"City": "New York",
"Street": "5th Avenue",
"Number": 87
}
}

 

 

Summary


That’s already the end of a little blog.
This blog was about using and implementing complex type

 

Links


Overview of blog series and link collection.

 

Appendix 1: Source code of Model file: DemoService.xml


 
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="demo" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Person">
<Key>
<PropertyRef Name="UniqueId" />
</Key>
<Property Name="UniqueId" Type="Edm.Int32" />
<Property Name="Name" Type="Edm.String"/>
<Property Name="HomeAddress" Type="demo.Address" />
</EntityType>

<ComplexType Name="Address">
<Property Name="City" Type="Edm.String"/>
<Property Name="Street" Type="Edm.String" />
<Property Name="Number" Type="Edm.Int32" />
</ComplexType>

<EntityContainer Name="container">
<EntitySet Name="People" EntityType="demo.Person" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

 

 

Appendix 2: Source code of Java class file: ServiceImplementation.java


 
package com.example.DemoService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.sap.cloud.sdk.service.prov.api.operations.Create;
import com.sap.cloud.sdk.service.prov.api.operations.Query;
import com.sap.cloud.sdk.service.prov.api.request.CreateRequest;
import com.sap.cloud.sdk.service.prov.api.request.QueryRequest;
import com.sap.cloud.sdk.service.prov.api.response.CreateResponse;
import com.sap.cloud.sdk.service.prov.api.response.QueryResponse;

public class ServiceImplementation {

private static final List<Map<String, Object>> peopleList = new ArrayList<Map<String, Object>>();


@Query(serviceName="DemoService", entity="People")
public QueryResponse getPeople(QueryRequest request) {
List<Map<String, Object>> peopleMap = getPeople();
return QueryResponse.setSuccess().setDataAsMap(peopleMap).response();
}


@Create(serviceName = "DemoService", entity = "People")
public CreateResponse createPerson(CreateRequest request) {
Map<String, Object> requestBody = request.getMapData();

// entity type properties for "Person"
int idToCreate = (int) requestBody.get("UniqueId");
String nameToCreate = (String) requestBody.get("Name");
Map<String, Object> homeAddress = (Map<String, Object>) requestBody.get("HomeAddress");

// complex type properties for "HomeAddress"
String cityToCreate = (String) homeAddress.get("City");
String streetToCreate = (String) homeAddress.get("Street");
int numberToCreate = (int) homeAddress.get("Number");

// do the actual creation in database
createPerson(idToCreate, nameToCreate, createAddress(cityToCreate, streetToCreate, numberToCreate));

return CreateResponse.setSuccess().response();
}



/* Dummy Data Store */


private List<Map<String, Object>> getPeople(){
if(peopleList.isEmpty()) {
createPerson(0, "Anna", createAddress("New York", "5th Avenue", 122));
createPerson(1, "Berta", createAddress("Mami", "Beach Avenue", 45));
createPerson(2, "Claudia", createAddress("Las Vegas", "Hopeful Avenue", 1));
createPerson(3, "Debbie", createAddress("New York", "6th Avenue", 133));
}

return peopleList;
}

private Map<String, Object> createAddress(String cityName, String streetName, int number){

Map<String, Object> addressMap = new HashMap<String, Object>();
addressMap.put("City", cityName);
addressMap.put("Street", streetName);
addressMap.put("Number", number);

return addressMap;
}

private void createPerson(int id, String name, Map<String, Object> addressMap){

Map<String, Object> personMap = new HashMap<String, Object>();
personMap.put("UniqueId", id);
personMap.put("Name", name);
personMap.put("HomeAddress", addressMap);

peopleList.add(personMap);
}

}