Skip to content

File Adapter Tutorial

ehennestad edited this page Aug 22, 2025 · 1 revision

Introduction

A FileAdapter is a plugin that defines how NANSEN reads and writes a specific file format. FileAdapters let you load and save data from different formats using simple, consistent commands—nansen.load() and nansen.save()—no matter which file type you’re working with. An internal registry matches each file to the appropriate adapter automatically. FileAdapters are easily extensible: users can add support for new formats and share them with others.

What you’ll learn

  • Load built-in demo data using FileAdapters.
  • Discover which adapters are available in your installation.
  • Create your first custom FileAdapter that returns a consistent table from multiple file types.
  • Extend an adapter to support additional extensions.
  • Save data with nansen.save.

Getting started with file adapters

Load your first file (MAT)

% Load a supported file format using NANSEN
data = nansen.load('patients.mat');

% Check what's in the data
data
You should see a MATLAB struct with fields like Gender, LastName, Age, etc.
data = 

  struct with fields:

                      Gender: {100×1 cell}
                    LastName: {100×1 cell}
                         Age: [100×1 double]
                      Weight: [100×1 double]
                      Smoker: [100×1 logical]
                    Systolic: [100×1 double]
                   Diastolic: [100×1 double]
                      Height: [100×1 double]
                    Location: {100×1 cell}
    SelfAssessedHealthStatus: {100×1 cell}

Note

In this example, NANSEN uses a MatFile adapter that wraps MATLAB’s load to read .mat files. For other file formats, we can use existing file adapters or create new ones (see Creating your first file adapter)

Load another format (Excel)

Let's try to load another file format. The patients demo file is also available as an excel file:

% Load another supported file format
data = nansen.load('patients.xls');

% Inspect the returned data
data
You should see a 100×10 table with the same variables.
data =

  100×10 table

      LastName         Gender      Age              Location               Height    Weight    Smoker    Systolic    Diastolic    SelfAssessedHealthStatus
    _____________    __________    ___    _____________________________    ______    ______    ______    ________    _________    ________________________

    {'Smith'    }    {'Male'  }    38     {'County General Hospital'  }      71       176      true        124          93             {'Excellent'}      
    {'Johnson'  }    {'Male'  }    43     {'VA Hospital'              }      69       163      false       109          77             {'Fair'     }      
    {'Williams' }    {'Female'}    38     {'St. Mary's Medical Center'}      64       131      false       125          83             {'Good'     }      
    {'Jones'    }    {'Female'}    40     {'VA Hospital'              }      67       133      false       117          75             {'Fair'     }      
    {'Brown'    }    {'Female'}    49     {'County General Hospital'  }      64       119      false       122          80             {'Good'     }      
    {'Davis'    }    {'Female'}    46     {'St. Mary's Medical Center'}      68       142      false       121          70             {'Good'     }      
    {'Miller'   }    {'Female'}    33     {'VA Hospital'              }      64       142      true        130          88             {'Good'     }      

          :              :          :                   :                    :         :         :          :            :                   :            

See which adapters are available

The core NANSEN toolbox supports a set of builtin file adapters. We can check out the list of available file adapters:

nansen.dataio.listFileAdapters()

>>

            FileAdapterName                                        FunctionName                                         SupportedFileTypes                     DataType             IsDynamic
    _______________________________    _____________________________________________________________________    ___________________________________    _________________________    _________

    "ExcelFile"                        "nansen.module.general.core.fileadapter.ExcelFile"                       ".xls"                                 "table"                        true   
    "MatFile"                          "nansen.module.general.core.fileadapter.MatFile"                         ".mat"                                 "struct"                       true   
    "ChiatahDemoFile"                  "nansen.module.ophys.twophoton.fileadapter.ChiatahDemoFile"              "h5"                                   "ImageStack"                   false  
    "ImageStack"                       "nansen.module.general.core.fileadapter.ImageStack"                      "ini, raw, tif, tiff, avi, h5, tsm"    "ImageStack"                   false  
    "Numpy2Mat"                        "nansen.module.general.core.fileadapter.Numpy2Mat"                       "npy"                                  "struct"                       false  
    "RoiGroup"                         "nansen.module.ophys.twophoton.fileadapter.RoiGroup"                     "mat, npy"                             "RoiGroup"                     false  
    "RoiSignalArray"                   "nansen.module.ophys.twophoton.fileadapter.RoiSignalArray"               "mat"                                  "RoiSignalArray"               false  
    "ScanImageMultiRoi2PSeries"        "nansen.module.ophys.twophoton.fileadapter.ScanImageMultiRoi2PSeries"    "tif, tiff"                            "ImageStack"                   false  
    "SciScanXYTSeries"                 "nansen.module.ophys.twophoton.fileadapter.SciScanXYTSeries"             "raw"                                  "ImageStack"                   false  

Note

The list shows adapters currently registered in your environment (core + any installed modules). It may look different from this tutorial. That’s expected.

Now that you’ve seen how the built‑in adapters work, you may run into a format that isn’t supported—or simply want more control over how your data is read. When that happens, the next step is to create a custom FileAdapter.

Creating your first file adapter

Let's revisit the patients.mat demo file, and create a file adapter that reads the data into a nicely formatted table instead of a structure.

nansen.createFileAdapter(...
    'Name', 'PatientsTable', ...
    'SupportedFileTypes', '.mat', ...
    'DataType', 'table', ... 
    'FileExpression', 'patients');

Tip

It is also possible to create a FileAdapter interactively using nansen.interactive.createFileAdapter()

After registration, MATLAB automatically creates this folder:

+PatientsTable/
├── fileadapter.json  % File adapter metadata
└── read.m            % Read function; opened automatically in editor

Fill in the read.m function:

function data = read(filename)
%READ Read patients.mat file and output a table
%
%   data = read(filename) reads a patients .mat file and returns a table
%   where each row represents patient information.

    S = load(filename);
    T = struct2table(S); % Convert to table

    % Convert text values to string
    T.LastName = string(T.LastName);
    T.Location = string(T.Location);

    % Convert discrete text values to categorical
    T.Gender = categorical(T.Gender);
    T.SelfAssessedHealthStatus = categorical(T.SelfAssessedHealthStatus);

    data = T; % Assign T to the data output
end

Now we can use the new file adapter to load the same file:

data = nansen.load('patients.mat', 'FileAdapter', 'PatientsTable');

% Display the data
data
You should see the same table as before but with better use of data types.
data =

  100×10 table

    Gender     LastName      Age    Weight    Smoker    Systolic    Diastolic    Height             Location              SelfAssessedHealthStatus
    ______    ___________    ___    ______    ______    ________    _________    ______    ___________________________    ________________________

    Male      "Smith"        38      176      true        124          93          71      "County General Hospital"             Excellent        
    Male      "Johnson"      43      163      false       109          77          69      "VA Hospital"                         Fair             
    Female    "Williams"     38      131      false       125          83          64      "St. Mary's Medical Center"           Good             
    Female    "Jones"        40      133      false       117          75          67      "VA Hospital"                         Fair             
    Female    "Brown"        49      119      false       122          80          64      "County General Hospital"             Good             

      :            :          :       :         :          :            :          :                    :                            :            

    Male      "Alexander"    25      171      true        128          99          69      "County General Hospital"             Good             
    Male      "Russell"      44      188      true        124          92          69      "VA Hospital"                         Good             
    Male      "Griffin"      49      186      false       119          74          70      "County General Hospital"             Fair             
    Male      "Diaz"         45      172      true        136          93          68      "County General Hospital"             Good             
    Male      "Hayes"        48      177      false       114          86          66      "County General Hospital"             Fair             

Note

Specifying the name-value pair 'FileAdapter', 'PatientsTable' is optional here, but is shown for demonstration. The file adapter registry should match the new file adapter based on the combination of the filename and the filetype, but sometimes multiple file adapters could exist for a given file type.

Editing a file adapter.

We could also use our new file adapter to load the excel file. Let's edit the file adapter:

nansen.editFileAdapter('PatientsTable')

This will change the current working directory in MATLAB into the folder where the file adapter definition is located.

  1. Add ".xls" to the SupportedFileTypes in fileadapter.json
    "SupportedFileTypes": [
      ".mat",
      ".xls"
    ],
  1. Replace line 7-8 in read.m with this snippet
    if endsWith(filename, '.mat')
        S = load(filename);
        T = struct2table(S); % Convert to table
    elseif endsWith(filename, '.xls')
        T = readtable(filename);
    end

Now we have a file adapter that can read both .mat and .xls files and present the data in the same way. Try it out:

data = nansen.load('patients.xls', 'FileAdapter', 'PatientsTable');

% Display the data
data

Creating file adapter for a custom file format (Advanced)

Example file format

Let's create an adapter for files with this structure:

example_data.myformat

{
  "metadata": {
    "description": "Sample time series data",
    "sampleRate": 1000,
    "channels": ["voltage", "current"]
  },
  "data": [
    [0.001, 1.2, 0.3],
    [0.002, 1.1, 0.35],
    [0.003, 0.9, 0.4]
  ]
}

Step 1: Register the adapter interactively

% Open the interactive registration dialog
nansen.interactive.createFileAdapter();

This opens a dialog where you fill in:

  • Name: MyFormat
  • File Extensions: myformat
  • Data Type: struct (MATLAB class of returned data)
  • Description: JSON format with metadata and numeric data

Step 2: Implement the read function

After registration, MATLAB automatically creates this structure:

+MyFormat/
├── fileadapter.json
└── read.m          % Opened automatically in editor

Fill in the read.m function:

function data = read(filename)
%READ Read MyFormat file
%   data = read(filename) reads a MyFormat file and returns a structure
%   with metadata and data fields.

    % Read JSON file
    fileContent = fileread(filename);
    jsonData = jsondecode(fileContent);
    
    % Extract metadata and data
    data = struct();
    data.metadata = jsonData.metadata;
    data.time = jsonData.data(:, 1);
    data.channels = struct();
    
    % Map data to channel names
    for i = 1:length(jsonData.metadata.channels)
        channelName = jsonData.metadata.channels{i};
        data.channels.(channelName) = jsonData.data(:, i+1);
    end
end

Step 3: Test your adapter

% Test with the example file
data = nansen.load('example_data.myformat');

% Verify the structure
disp(data.metadata)
plot(data.time, data.channels.voltage)

Step 4: Add Write Support (Optional)

Create write.m in the same directory:

function write(filename, data)
%WRITE Write data to MyFormat file
%   write(filename, data) saves data to a MyFormat JSON file

    % Reconstruct the file format
    output = struct();
    output.metadata = data.metadata;
    
    % Combine time and channel data
    channelNames = fieldnames(data.channels);
    dataMatrix = [data.time];
    
    for i = 1:length(channelNames)
        dataMatrix = [dataMatrix, data.channels.(channelNames{i})];
    end
    
    output.data = dataMatrix;
    
    % Write JSON file
    jsonText = jsonencode(output, 'PrettyPrint', true);
    
    fid = fopen(filename, 'w');
    fprintf(fid, '%s', jsonText);
    fclose(fid);
end

Saving data

% Save data as a mat file
nansen.save('output.mat', data);

% Save data in your custom format
nansen.save('output.myformat', data);

Next steps (Coming soon)

  • Explanation: How the adapter registry resolves files and precedence rules.
  • How-to guides:
    • Package and share an adapter
    • Add write support and round-trip tests
    • Handle large files and streaming reads
  • Reference: Minimal fileadapter.json schema and adapter API.

Clone this wiki locally