Main Content

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+' 
This code creates a scalar structure (S) with three fields:
S = 
    name: 'Ed Plum'
    score: 83
    grade: 'B+'
A multidimensional structure array can be created by inserting additional elements:
S(2).name = 'Toni Miller';
S(2).score = 91;
S(2).grade = 'A-';
In this case, a structure array of dimensions (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.

 Java Class Student

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:

  1. Create MWHttpClient and associated proxy (using createProxy) as shown in Create MATLAB Production Server Java Client Using MWHttpClient Class.

  2. 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.

  3. 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 both get and set methods for the fields depends on whether it is being used as input or output or both. In this example, note that when student is passed as an input to method sortstudents, only the get 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.