Clear Filters
Clear Filters

Managing custom classes, mat-files, and namespaces

34 views (last 30 days)
John
John on 1 Aug 2024 at 15:18
Commented: John on 2 Aug 2024 at 13:41
Say I have a (simple) custom class, defined with a classdef. Let's say it's defined in my_class.m
I then generate a bunch of objects of the class, and save the results to a matfile: my_data.mat.
I then want to reorganize my code, either by:
  1. renaming my class (but otherwise keeping the definition the same). Now I have my_class_ver1.m as the classdef.
  2. introducing namespaces. Here, I've moved the file, my_class.m, into a namespace directory, i.e.,: +ver1/my_class.m
Is there any way to provide a class (re)mapping when loading the existing my_data.mat file? As it stands, Matlab just loads the file as an int a uint32 array. For #2, just importing the namespace (import ver1.* or import ver1.my_class) does not work.

Accepted Answer

Steven Lord
Steven Lord on 1 Aug 2024 at 19:38
For at least the first of those scenarios, class aliasing may be of use. That documentation page has a section "Renaming a Namespace" that may also address the second of your scenarios.
  3 Comments
Steven Lord
Steven Lord on 1 Aug 2024 at 20:57
Edited: Steven Lord on 1 Aug 2024 at 21:01
That page states "MATLAB recognizes both FirstName and SecondName as the same class as long as SecondName.m and the associated alias resources folder are in the same folder on the MATLAB path." so I believe you can move the resources directory elsewhere as long as you move SecondName.m alongside it.
[Removed something I'd written about FirstName.m after rereading the page, as it says "In fact, you must remove the original definition from the path so that MATLAB finds the newer alias instead of the older definition."]
John
John on 2 Aug 2024 at 13:41
The aliasing functionality seems a litte tempermental about paths. For instance, if I have the following directory layout:
  • my_dir_1 contains the +ver1/ directory (which contains +ver1/my_class.m)
  • my_dir_2 is on my Matlab path
In this scenario I can write the alias file from my_dir_1, and then move both resources and +ver1/ to my_dir_2. This works.
However if I start by moving +ver1 over to my_dir_2, the writeAliasFile method will error out.
To make things a bit more complicated, now I have two classes and two namespaces:
  • my_class1.m, has moved to +ver1
  • my_class2.m, has moved to +ver2
If I only alias one class, Matlab cannot find the other.
If I alias them both at the same time, i.e.,
fileMgr = matlab.alias.AliasFileManager;
addAlias(fileMgr, NewName='ver1.my_class1',OldName='my_class1')
addAlias(fileMgr, NewName='ver2.my_class2',OldName='my_class2')
writeAliasFile(fileMgr)
then everything is fine. But things are strange if I write the aliases one at a time:
clear all
fileMgr = matlab.alias.AliasFileManager;
addAlias(fileMgr, NewName='ver1.my_class1',OldName='my_class1')
writeAliasFile(fileMgr)
clear all
fileMgr = matlab.alias.AliasFileManager;
addAlias(fileMgr, NewName='ver2.my_class2',OldName='my_class2')
writeAliasFile(fileMgr)
clear all
The alias.json file generated in the resources directory only contains mention of the my_class2 alias (the writeFile seems to overwrite, rather than append). But somehow this still works, even after restarting Matlab.

Sign in to comment.

More Answers (1)

Shubham
Shubham on 1 Aug 2024 at 15:38
Hi John,
When you rename a class or move it into a namespace, MATLAB does not automatically recognize the new class definition when loading old .mat files containing objects of the original class. However, you can use MATLAB's matfile and loadobj functionalities to handle this situation.
Scenario 1: Renaming the Class
  1. Define the new class with a loadobj method: Create a loadobj method in your new class definition to handle the conversion from the old class to the new class.
% my_class_ver1.m
classdef my_class_ver1
properties
% Define your properties here
end
methods
function obj = my_class_ver1()
% Constructor code
end
end
methods (Static)
function obj = loadobj(a)
if isa(a, 'my_class')
% Convert from my_class to my_class_ver1
obj = my_class_ver1();
% Copy properties from a to obj as needed
else
obj = a;
end
end
end
end
2. Load the .mat file: When you load the .mat file, MATLAB will call the loadobj method to convert the objects.
data = load('my_data.mat');
% Access your objects from the data struct
Scenario 2: Introducing Namespaces
  1. Define the new class within the namespace with a loadobj method: Create a loadobj method in your new class definition within the namespace to handle the conversion.
% +ver1/my_class.m
classdef my_class
properties
% Define your properties here
end
methods
function obj = my_class()
% Constructor code
end
end
methods (Static)
function obj = loadobj(a)
if isa(a, 'my_class')
% Convert from my_class to ver1.my_class
obj = ver1.my_class();
% Copy properties from a to obj as needed
else
obj = a;
end
end
end
end
2. Load the .mat file: Similar to the first scenario, MATLAB will call the loadobj method to convert the objects when you load the file.
data = load('my_data.mat');
% Access your objects from the data struct
Important Points to consider:
  • Ensure that the properties and methods of the new class match those of the old class to facilitate smooth conversion.
  • You might need to manually copy the properties from the old object to the new object within the loadobj method.
  • This approach requires you to have access to the old class definition during the transition period. If the old class definition is not available, you will need to manually handle the conversion.
  3 Comments
Shubham
Shubham on 1 Aug 2024 at 19:11
John,
You're right, the loadobj method of the new class won't be called directly if the .mat file contains objects of the old class, and MATLAB will look for the old class definition.
I think your approach is right that you can temporarily add the legacy class definitions to the path when loading and converting your data.
An Idea for writing a code would look like this:
function new_data = load_and_convert(filename, legacy_path, new_class_map)
% Temporarily add the legacy class definitions to the path
old_path = addpath(legacy_path);
% Load the data
legacy_data = load(filename);
% Convert the data
new_data = structfun(@(obj) convert_legacy_object(obj, new_class_map), legacy_data, 'UniformOutput', false);
% Restore the original path
path(old_path);
end
function new_obj = convert_legacy_object(obj, new_class_map)
% Check if the object is an instance of a legacy class
old_class_name = class(obj);
if isfield(new_class_map, old_class_name)
new_class_name = new_class_map.(old_class_name);
new_obj = feval(new_class_name); % Create an instance of the new class
% Copy properties from the old object to the new object
% Note: This assumes the properties have the same names and types
props = properties(obj);
for i = 1:numel(props)
new_obj.(props{i}) = obj.(props{i});
end
else
% If the object is not a legacy class, return it unchanged
new_obj = obj;
end
end
You can use it like this:
First, define the mapping of legacy class to new class:
new_class_map = struct();
new_class_map.my_class = 'my_class_ver1';
% Add more mappings as needed
Then, call the load_and_convert function:
legacy_path = '/path/to/legacy/classdefs';
filename = 'my_data.mat';
new_data = load_and_convert(filename, legacy_path, new_class_map);
This should let you load and convert legacy data without needing to keep the old class definitions permanently on the path. It handles multiple legacy classes with minimal changes. It might work in your case.
John
John on 1 Aug 2024 at 20:01
Shubham,
I appreciate your attention, but I think I'll take Steven's answer (below) as it's a little closer to what I was looking for.

Sign in to comment.

Categories

Find more on Data Type Identification in Help Center and File Exchange

Products


Release

R2024a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!