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
MWHttpClient
and 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 get
methods on the fields in the Java class. Whether or not the class needs bothget
andset
methods for the fields depends on whether it is being used as input or output or both. In this example, note that whenstudent
is passed as an input to methodsortstudents
, only theget
methods 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
set
methods or@ConstructorProperties
annotation provided by Java.get
methods 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.