Article
· Feb 27, 2023 4m read

Accessing Properties of a Relationship in a JSON-enabled Persistent Class

Setting the Scene

Suppose you have the following 2 persistent classes that are both JSON-enabled (i.e. extends %JSON.Adaptor or %pkg.isc.rest.model.adaptor)

Class Test.Employee Extends (%Persistent, %pkg.isc.rest.model.adaptor) { 
  Parameter RESOURCENAME = "employee";
  Parameter firstName As %String;
  Parameter lastName As %String; 
  Relationship projects As Test.Project [ Cardinality = many, Inverse = employee) ];
}

Class Test.Project Extends (%Persistent, %pkg.isc.rest.model.adaptor) {
  Parameter RESOURCENAME = "project"; 
  Parameter name As %String; 
  Relationship employee As Test.Employee [ Cardinality = one, Inverse = projects, OnDelete = setnull ];
}

In this example, an Employee can have multiple projects but each project can only be delegated to one employee. 

Now imagine we have the following objects stored in our database

Employees table:

ID firstName lastName
1 John Doe
2 Jane Fonda

Projects table:

ID name employee
1 Project Alpha 1
2 Project Beta 2
3 Project Charlie 2

  

In this example, John Doe is the assigned employee for Project Alpha and Jane Fonda is the assigned employee for Project Beta and Project Charlie.

 

The Problem: Can't access Relationship property via REST requests

If you run a basic GET request to get all of the Test.Project objects stored in the database, you'll notice that Relationship properties aren't included in the returned JSON.

The returned JSON array will look something like this:

[
  {"_id": "1", "name": "Project Alpha" },
  {"_id": "2", "name": "Project Beta" },
  {"_id": "3", "name": "Project Charlie" },
]

 

How do we get the details of a project's assigned employee?

We want to override the default value written to the console when %JSONExport() is called on an object.

To do so, we update the Relationship definitions in the classes:

Test.Employee 

Relationship projects As Test.Project(%JSONINCLUDE= "none" ) [ Cardinality = many, Inverse = employee) ];

XData BasicReference [ XMLNamespace = "http://www.intersystems.com/apps/jsonmapping" ]
{
  <Mapping xmlns="http://www.intersystems.com/apps/jsonmapping">
    <Property Name = "firstName" />
    <Property Name = "lastName" />
  </Mapping>
}

Notice that we add set %JSONINCLUDE = "none" in the projects Relationship in Test.Employee. This is to avoid creating an infinite loop (e.g. writing the JSON details of a Test.Project writes the JSON details of the assigned Test.Empoyee which will write the JSON details of the employee's assigned Test.Project(s), etc)

Test.Project 

Relationship employee As Test.Employee(%JSONINCLUDE = "INOUT", %JSONMAPPING = "BasicReference", %JSONREFERENCE = "OBJECT") [ Cardinality = one, Inverse = projects, OnDelete = setnull ]; 

We set %JSONINCLUDE = "INOUT" in the employee relation in Test.Project. This says that the employee Relationship should be included in the JSON input and output.

We also set %JSONMAPPING = "BasicReference" and %JSONREFERENCE = "OBJECT".

Without defining %JSONREFERENCE, exporting Project Alpha to a JSON object will only return the employee ID:

{"_id": "1", "name": "Project Alpha", "employee": "1" },

If more properties get added to Test.Employee later on (e.g. "dob"), they will be included in the returned JSON. Setting %JSONMAPPING = "BasicReference" allows us to specify which properties we want included in the returned JSON for employee.

{"_id": "1", "name": "Project Alpha", "employee": {"_id": "1", "firstName": "John", "lastName": "Doe"} }

 

Wrapping it Up

Now when we run a basic GET request for all of the Test.Project objects in the stored database, the returned JSON array will include the relationship properties

[   
  {"_id": "1", "name": "Project Alpha", "employee": {"_id": "1", "firstName": "John", "lastName": "Doe" },   
  {"_id": "2", "name": "Project Beta", "employee": {"_id": "2", "firstName": "Jane", "lastName": "Fonda" },  
  {"_id": "3", "name": "Project Charlie", "employee": {"_id": "2", "firstName": "Jane", "lastName": "Fonda" },  
]

Final Code

Class Test.Employee Extends (%Persistent, %pkg.isc.rest.model.adaptor) {   
  Parameter RESOURCENAME = "employee";   
  Parameter firstName As %String;   
  Parameter lastName As %String;   
  Relationship projects As Test.Project(%JSONINCLUDE= "none" ) [ Cardinality = many, Inverse = employee) ];

  XData BasicReference [ XMLNamespace = "http://www.intersystems.com/apps/jsonmapping" ]
  {
    <Mapping xmlns="http://www.intersystems.com/apps/jsonmapping">
      <Property Name = "firstName" />
      <Property Name = "lastName" />
    </Mapping>
  }
} 

Class Test.Project Extends (%Persistent, %pkg.isc.rest.model.adaptor) {   
  Parameter RESOURCENAME = "project";   
  Parameter name As %String;   
  Relationship employee As Test.Employee(%JSONINCLUDE = "INOUT", %JSONMAPPING = "BasicReference", %JSONREFERENCE = "OBJECT") [ Cardinality = one, Inverse = projects, OnDelete = setnull ]; 
}

 

A Step Further: Updating a Relationship property via a POST/PUT request

If we want to set the assigned Employee for a Project via a POST/PUT request (when creating or editing a project), we simply include a key-value pair in the request body where the key is the class RESOURCENAME and the value is a JSON that follows the BasicReference mapping for a Test.Employee 

See example request body below for updating the assigned employee for Project 3 from Jane Fonda to John Doe:

{
  "_id": "3",
  "name": "Project Charlie",
  "employee": { "_id": "1", "firstName": "John", "lastName": "Doe" }
}
Discussion (0)1
Log in or sign up to continue