Main Content

Transitioning Serialization and Deserialization Processes to matlab.mixin.CustomElementSerialization

The recommended process for customizing serialization and deserialization of objects is to use the matlab.mixin.CustomElementSerialization mixin (since R2024b). Transitioning to the mixin from an existing implementation of loadobj and saveobj can give you greater control of the serialization process in future revisions of the class. However, some existing implementations might make loadobj and saveobj the better option.

Preserve Version Compatibility Between loadobj and saveobj Implementations and matlab.mixin.CustomElementSerialization

The first version of the Rectangle class represents a rectangle using the coordinates of the lower-left and upper-right vertices. The saveobj and loadobj methods only handle the property data using structures. This implementation enables flexibility for any future class versions that use different properties to define the rectangle.

classdef Rectangle  % Version 1
    properties
        x1
        y1
        x2
        y2
    end  
    methods
        function obj = Rectangle(x1,y1,x2,y2)
            obj.x1 = x1;
            obj.y1 = y1;
            obj.x2 = x2;
            obj.y2 = y2;
        end
        function s = saveobj(obj)
            s.x1 = obj.x1;
            s.y1 = obj.y1;
            s.x2 = obj.x2;
            s.y2 = obj.y2;
        end
    end
 
    methods(Static)
        function obj = loadobj(s)
            obj = Rectangle(s.x1,s.y1,s.x2,s.y2);
        end
    end
end

Version 2 of the class represents the rectangle using the coordinates of the lower-left vertex, the height, and the width. The revision maintains compatibility by implementing saveobj and loadobj methods to ensure that Version 2 of the class can load instances saved under Version 1, and Version 1 of the class can load instances saved under Version 2. The constructor accepts the same property values of Version 1, namely the coordinates of the lower-left and upper-right coordinates. The loadobj method calls the Rectangle constructor with those values. To preserve compatibility with Version 1, the saveobj method converts the new properties back to the old properties and saves them as a structure.

classdef Rectangle  % Version 2
    properties
        x0
        y0
        width
        height
    end  
    methods
        function obj = Rectangle(x1,y1,x2,y2)
            obj.x0 = x1;
            obj.y0 = y1;
            obj.width = x2 - x1;
            obj.height = y2 - y1;
        end
        function s = saveobj(obj)
            s.x1 = obj.x0;
            s.y1 = obj.y0;
            s.x2 = obj.x0 + obj.width;
            s.y2 = obj.y0 + obj.height;
        end
    end
     
    methods(Static)
        function obj = loadobj(s)
             obj = Rectangle(s.x1,s.y1,s.x2,s.y2);
        end
    end 
end

Version 2a of the Rectangle class is a subclass of matlab.mixin.CustomElementSerialization, but its behavior is consistent with Version 2. The modifyOutgoingSerializationContent method stores the data in properties consistent with Version 1 of the class. With this method in place:

  • Both Version 1 and Version 2 of the class can load instances saved under Version 2a because the property values are stored in the original format of Version 1.

  • modifyOutgoingSerializationContent saves the rectangle information as x0, y0, width, and height by default, but it also saves the coordinates x1, x2, y1, and y2. Because Version 2a includes the properties and values in the same format as Versions 1 and 2, Version 2a can load an instance of any version.

classdef Rectangle < matlab.mixin.CustomElementSerialization  % Version 2a
    properties
        x0
        y0
        width
        height
    end  
    methods
        function obj = Rectangle(x1,y1,x2,y2)
            obj.x0 = x1;
            obj.y0 = y1;
            obj.width = x2 - x1;
            obj.height = y2 - y1;
        end
    end
 
    methods(Static)
        function modifyOutgoingSerializationContent(sObj,obj)
            sObj.addNameValue("x1",obj.x0);
            sObj.addNameValue("x2",obj.x0 + obj.width);
            sObj.addNameValue("y1",obj.y0);
            sObj.addNameValue("y2",obj.y0 + obj.height);
        end
    end
end

With this new version, you can now take advantage of all of the capabilities of matlab.mixin.CustomElementSerialization. To check the compatibility, create an instance of Rectangle under Version 2a.

test = Rectangle(1,1,4,6)
test = 

  Rectangle with properties:

        x0: 1
        y0: 1
     width: 3
    height: 5

Save the instance. modifyOutgoingSerializationContent uses the width and height properties to define the second vertex.

save("yourfilepath/RectV2a.mat","test")

Clear the instance, and then load it under Version 1.

clear test
load("yourfilepath/RectV2a.mat","test")
test
test = 

  Rectangle with properties:

    x1: 1
    y1: 1
    x2: 4
    y2: 6

Private Properties with the Same Name in Class Hierarchies

Classes in a hierarchy that define private properties with the same name require special handling when serializing and deserializing with loadobj and saveobj. The Animal class and its subclass Pet both define a private property named ID. To handle both properties:

  • The saveobj method of Animal saves its ID property in the outgoing structure under the name of AnimalID to avoid conflicting with the ID property of Pet.

  • The loadobj methods of both classes pass the value of AnimalID to the constructor to recreate the ID property of Animal.

classdef Animal
    properties
        Species
    end
    properties(Access = private)
        ID
    end
 
    methods
        function obj = Animal(Species,ID)
            obj.Species = Species;
            obj.ID = ID;
        end
        function s = saveobj(obj)
            s.Species = obj.Species;
            s.AnimalID = obj.ID;
        end
    end
    methods (Static)
        function obj = loadobj(s)
            if isstruct(s)
                obj = Animal(s.Species,s.AnimalID);
            else
                obj = s;
            end
        end
    end
end
classdef Pet < Animal
    properties
        OwnerName
    end
    properties(Access = private)
        ID
    end
 
    methods
        function obj = Pet(Species,AnimalID,OwnerName,ID)
            obj@Animal(Species,AnimalID);
            obj.OwnerName = OwnerName;
            obj.ID = ID;
        end
        function s = saveobj(obj)
            s = saveobj@Animal(obj);
            s.OwnerName = obj.OwnerName;
            s.ID = obj.ID;
        end
    end
     
    methods(Static)
        function obj = loadobj(s)
            if isstruct(s)
                obj = Pet(s.Species,s.AnimalID,s.OwnerName,s.ID);
            else
                obj = s;
            end
        end
    end
end

The matlab.mixin.CustomElementSerialization mixin enables you to reference private properties with the same name by preceding the property name with the name of the defining class and a dot. You can use this convention to transition classes with private property overlap to use the mixin instead of saveobj and loadobj.

For example, the following revised Animal class inherits from the mixin and implements a modifyIncomingSerializationContent method. The saveobj method of the original Animal stores its ID property as AnimalID. The modifyIncomingSerializationContent method checks for an AnimalID field in the incoming content. If AnimalID is part of the serialized content, the modifyIncomingSerializationContent method renames the property to Animal.ID. The rename method uses the className.propertyName syntax that is required for private properties. (For more information, see the description for rename in matlab.serialization.ElementSerializationContent.)

classdef Animal < matlab.mixin.CustomElementSerialization
    properties
        Species
    end
    properties (Access = private)
        ID
    end
 
    methods
        function obj = Animal(Species,ID)
            obj.Species = Species;
            obj.ID = ID;
        end
    end
 
    methods(Static)
        function modifyIncomingSerializationContent(sObj)
            if sObj.hasNameValue("AnimalID")
                sObj.rename("AnimalID","Animal.ID");
            end
        end
    end
end
The revised Pet class implements a modifyIncomingSerializationContent method that calls the superclass method to process the superclass ID property. To enable the older version of the class to deserialize objects saved under the new definition, Pet implements modifyOutgoingSerializationContent to save the superclass ID property as AnimalID.
classdef Pet < Animal
    properties
        OwnerName
    end
    properties(Access = private)
        ID
    end
    methods
        function obj = Pet(Species,AnimalID,OwnerName,ID)
            obj@Animal(Species,AnimalID);
            obj.OwnerName = OwnerName;
            obj.ID = ID;
        end
    end
     
    methods(Static)
        function modifyOutgoingSerializationContent(sObj,~)
            sObj.addNameValue("AnimalID",sObj.getValue("Animal.ID"));
            sObj.remove("Animal.ID");
        end
        function modifyIncomingSerializationContent(sObj)
            modifyIncomingSerializationContent@Animal(sObj)
        end
    end
end

Maintaining Existing loadobj and saveobj Implementations

In some cases, existing designs using loadobj and saveobj cannot be easily transitioned to a matlab.mixin.CustomElementSerialization design without significant revision of existing classes. For example:

  • The loadobj method of an existing class only accepts a structure as input. Deserializing an object under the old definition might cause warnings. To force loadobj to deserialize the content as a structure, you can use modifyOutgoingSerializationContent to add a property that is not part of the original class definition. The disadvantage to this method is that the added property is now stored, even though it has no meaning in either version of the class.

  • The loadobj method can only deserialize data from a format other than an object or a structure. For example, a class could use saveobj to save property values in a table, and the corresponding loadobj can only accept a table as input. The methods of matlab.mixin.CustomElementSerialization only support object data, so data from the older version of the class would be inaccessible. In this case, the only workaround is to revise how the existing class definition serializes data.

In cases like these, maintaining your existing loadobj and saveoobj implementations might be preferable to what would be required to transition to matlab.mixin.CustomElementSerialization.

See Also

| |

Related Topics