Marshal MATLAB Structures (Structs) in Java
Structures (or structs) are MATLAB® arrays with elements accessed by textual field designators.
Structs consist of data containers, called fields. Each field stores an array of some MATLAB data type. Every field has a unique name.
A field in a structure can have a value compatible with any MATLAB data type, including a cell array or another structure.
In MATLAB, a structure is created as follows:
S.name = 'Ed Plum'; S.score = 83; S.grade = 'B+'
S) with three fields: S =
name: 'Ed Plum'
score: 83
grade: 'B+'S(2).name = 'Toni Miller'; S(2).score = 91; S(2).grade = 'A-';
(1,2) is created. Structs with additional
dimensions are also supported.Since Java® does not natively support MATLAB structures, marshaling structs between the server and client involves additional coding.
Marshaling a Struct Between Client and Server
MATLAB structures are ordered lists of name-value pairs. You represent them in Java with a class using fields consisting of the same case-sensitive names.
The Java class must also have public get and set methods
defined for each field. Whether or not the class needs both get and set methods
depends on whether it is being used as input or output, or both.
Following is a simple example of how a MATLAB structure can be marshaled between Java client and server.
In this example, MATLAB function sortstudents takes
in an array of structures (see Marshal MATLAB Structures (Structs) in Java for details).
Each element in the struct array represents different information
about a student. sortstudents sorts the input array
in ascending order by score of each student, as follows:
function sorted = sortstudents(unsorted)
% Receive a vector of students as input
% Get scores of all the students
scores = {unsorted.score};
% Convert the cell array containing scores into a numeric array or doubles
scores = cell2mat(scores);
% Sort the scores array
[s i] = sort(scores);
% Sort the students array based on the sorted scores array
sorted = unsorted(i);Note
Even though this example only uses the scores field
of the input structure, you can also work with name and grade fields
in a similar manner.
You package sortstudents into a deployable archive
(scoresorter.ctf) using the Production Server Compiler app
(see Create Deployable Archive for MATLAB Production Server for details) and make it available on the server at
http://localhost:9910/scoresorter for access by the
Java client (see Deploy Archive to MATLAB Production Server).
Before defining the Java interface required by the client,
define the MATLAB structure, Student, using
a Java class.
Student declares the fields name, score and grade with
appropriate types. It also contains public get and set functions
to access these fields.
Next, define the Java interface StudentSorter,
which calls method sortstudents and uses the Student class
to marshal inputs and outputs.
Since you are working with a struct type, Student must be included in the
annotation MWStructureList.
interface StudentSorter {
@MWStructureList({Student.class})
Student[] sortstudents(Student[] students)
throws IOException, MATLABException;
}
Finally, you write the Java application (MPSClientExample)
for the client:
Create
MWHttpClientand associated proxy (usingcreateProxy) as shown in Create MATLAB Production Server Java Client Using MWHttpClient Class.Create an unsorted student struct array in Java that mimics the MATLAB struct in naming, number of inputs and outputs, and type validity in MATLAB. See Java Client Coding Best Practices for more information.
Sort the student array and display it.
import java.net.URL;
import java.io.IOException;
import com.mathworks.mps.client.MWClient;
import com.mathworks.mps.client.MWHttpClient;
import com.mathworks.mps.client.MATLABException;
import com.mathworks.mps.client.annotations.MWStructureList;
interface StudentSorter {
@MWStructureList({Student.class})
Student[] sortstudents(Student[] students)
throws IOException, MATLABException;
}
public class ClientExample {
public static void main(String[] args){
MWClient client = new MWHttpClient();
try{
StudentSorter s =
client.createProxy(new URL("http://localhost:9910/scoresorter"),
StudentSorter.class );
Student[] students = new Student[]{new Student("Toni Miller", 90, "A"),
new Student("Ed Plum", 80, "B+"),
new Student("Mark Jones", 85, "A-")};
Student[] sorted = s.sortstudents(students);
System.out.println("Student list sorted in the
ascending order of scores : ");
for(Student st:sorted){
System.out.println(st);
}
}catch(IOException ex){
System.out.println(ex);
}catch(MATLABException ex){
System.out.println(ex);
}finally{
client.close();
}
}
}
Map Java Field Names to MATLAB Field Names
Java classes that represent MATLAB structures use the Java Beans Introspector
class (https://docs.oracle.com/javase/6/docs/api/java/beans/Introspector.html)
to map properties to fields and its default naming conventions are used.
This means that by default its decapitalize() method
is used. This maps the first letter of a Java field into a lower
case letter. By default, it is not possible to define a Java field
which will map to a MATLAB field which starts with an upper
case.
You can override this behavior by implementing a BeanInfo class
with a custom getPropertyDescriptors() method.
For example:
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;
public class StudentBeanInfo extends SimpleBeanInfo
{
@Override
public PropertyDescriptor[] getPropertyDescriptors()
{
PropertyDescriptor[] props = new PropertyDescriptor[3];
try
{
// name uses default naming conventions so we do not need to
// explicitly specify the accessor names.
props[0] = new PropertyDescriptor("name",MyStruct.class);
// score uses default naming conventions so we do not need to
// explicitly specify the accessor names.
props[1] = new PropertyDescriptor("score",MyStruct.class);
// Grade uses a custom naming convention so we do need to
// explicitly specify the accessor names.
props[1] = new PropertyDescriptor("Grade",MyStruct.class,
"getGrade","setGrade");
return props;
}
catch (IntrospectionException e)
{
e.printStackTrace();
}
return null;
}
}Defining MATLAB Structures Only Used as Inputs
When defining Java structs as inputs, follow these guidelines:
Ensure that the fields in the Java class match the field names in the MATLAB struct exactly. The field names are case sensitive.
Use
public getmethods on the fields in the Java class. Whether or not the class needs bothgetandsetmethods for the fields depends on whether it is being used as input or output or both. In this example, note that whenstudentis passed as an input to methodsortstudents, only thegetmethods for its fields are used by the data marshaling algorithm.
As a result, if a Java class is defined for a MATLAB structure
that is only used as an input value, the set methods
are not required. This version of the Student class
only represents input values:
public class Student{
private String name;
private int score;
private String grade;
public Student(String name, int score, String grade){
this.name = name;
this.score = score;
this.grade = grade;
}
public String getName(){
return name;
}
public int getScore(){
return score;
}
public String getGrade(){
return grade;
}
}
Defining MATLAB Structures Only Used as an Output
When defining Java structs as outputs, follow these guidelines:
Ensure that the fields in the Java class match the field names in the MATLAB struct exactly. The field names are case sensitive.
Create a new instance of the Java class using the structure received from MATLAB. Do so by using
setmethods or@ConstructorPropertiesannotation provided by Java.getmethods are not required for a Java class when defining output-only MATLAB structures.
An output-only Student class using set methods
follows:
public class Student{
private String name;
private int score;
private String grade;
public void setName(String name){
this.name = name;
}
public void setScore(int score){
this.score = score;
}
public void setGrade(String grade){
this.grade = grade;
}
}
An output-only Student class using @ConstructorProperties follows:
public class Student{
private String name;
private int score;
private String grade;
@ConstructorProperties({"name","score","grade"})
public Student(String n, int s, String g){
this.name = n;
this.score = s;
this.grade = g;
}
}Note
If both set methods and @ConstructorProperties annotation
are provided, set methods take precedence over @ConstructorProperties annotation.
Defining MATLAB Structures Used as Both Inputs and Outputs
If the Student class is used as both an input
and output, you need to provide get methods to
perform marshaling to MATLAB. For marshaling from MATLAB,
use set methods
or @ConstructorProperties annotation.
