Code covered by the BSD License  

Highlights from
Use Mac OS X XML Plists with MATLAB (Updated Version)

Use Mac OS X XML Plists with MATLAB (Updated Version)

by

 

A updated version of that by John Iversen

XMLPlistToStruct(xml)
function [S KT] = XMLPlistToStruct(xml)
% XMLPlistToStruct  Parse Mac OSX XML property list into matlab structure
%
%   S = XMLPlistToStruct(xmlText)
%
%       return structure S from OS X style XML property list in string xmlText
%
%           The mapping is straightforward:
%           dict -> structure
%           array -> cell array
%           property list keys become field names
%               (note: keys may only contain alphanumeric or space chars)
%           integer, real -> double
%           string, data, date -> string
%
%       Note: data and date are treated as strings, with a suffix (_DATA or
%               _DATE) added to the field name indicating the original type, 
%               similarly _INTEGER is added for integers. This ensures
%               that the original type is preserved if we convert the 
%               structure back to XML.
%
%   See the example plist and usage at the end of this file
%
%   Details of XML property list format are found at:
%   http://developer.apple.com/documentation/Cocoa/Conceptual/PropertyLists/Concepts/XMLPListsConcept.html
%   http://www.apple.com/DTDs/PropertyList-1.0.dtd
%
%   JRI 3/14/05 (John R. Iversen <iversen@nsi.edu>)

dictLevel = 0;
inArray = 0;
arrayIndex = 0;
key = {};

ibra = findstr(xml,'<');
iket = findstr(xml,'>');

if (length(ibra) ~= length(iket)),
    error('unmatched brackets')
end

itag = 1;
ikey =1;
while (itag <= length(ibra)),
    
    value = [];
    valueStr = '';
    keySuffix = '';
    tag = xml( (ibra(itag)+1):(iket(itag)-1) );
    
    %split tag on first space into tag proper and additional information
    iSpace = strfind(tag, ' ');
    if ~isempty(iSpace),
        tagInfo = tag( (iSpace(1)+1):end );
        tag = tag(1:(iSpace(1)-1));
    end
    
    %if we're in an array, increase the index by one
    if (dictLevel > 0) & inArray(dictLevel),
       arrayIndex(dictLevel) = arrayIndex(dictLevel) + 1;
    end

    switch tag,
        
        case '!DOCTYPE', %make sure it's a plist
            if isempty(findstr(tagInfo, 'plist')),
                error('This is not an Apple plist')
            end
                
        case {'?xml', 'plist', '/plist'},
            %skip other header tags--assume they, as well as outer <plist></plist>, are correct
            
        case 'dict',
            dictLevel = dictLevel + 1;
            inArray(dictLevel) = 0;
            
        case '/dict',
            key(dictLevel) = [];
            if (inArray(dictLevel) ~= 0), error('Array not closed by end of dict'); end
            dictLevel = dictLevel - 1;
            
        case 'key',
%             keyname = strrep(xml( (iket(itag)+1):(ibra(itag+1)-1) ), ' ', '_');%space->_
            keyname = xml( (iket(itag)+1):(ibra(itag+1)-1) );
            key{dictLevel} = genvarname(keyname);% generate valid variable name.
            
            KT{ikey,1} = keyname;
            KT{ikey,2} = key{dictLevel};
            ikey = ikey + 1;
            itag = itag + 1; %skip to close
            ctag = xml( (ibra(itag)+1):(iket(itag)-1) );
            if ~strcmp(ctag, ['/' tag]), error(['<' tag '> not properly closed']); end
            
        case 'array',
            inArray(dictLevel) = inArray(dictLevel) +1;
%             arrayIndex(dictLevel) = 0;
            arraystr = [arraystr '{'];
            
        case '/array',
            inArray(dictLevel) = inArray(dictLevel) +1;
%             arrayIndex(dictLevel) = 0;
            arraystr = [arraystr '}'];

        case 'array/', %empty array
            value = { [] };
                
        case 'true/'
            value = true; %logical

        case 'false/'
            value = false; %logical

        case {'string', 'date', 'data'},
            valueStr = xml( (iket(itag)+1):(ibra(itag+1)-1) );
            
            valueStr = feval(sprintf('c%s',upper(tag)), valueStr);
            
            itag = itag + 1; %skip to close
            closetag = xml( (ibra(itag)+1):(iket(itag)-1) );
            if ~strcmp(closetag, ['/' tag]), error(['<' tag '> not properly closed']); end
            %add suffix to indicate original type
            if strcmp(tag, 'date') | strcmp(tag, 'data'),
%                 keySuffix = ['__' upper(tag)];
%                  keySuffix = upper(tag);

            end
            
            keySuffix = upper(tag);

        case {'integer', 'real'}
            valueStr = xml( (iket(itag)+1):(ibra(itag+1)-1) );
            value = str2num(valueStr);
            itag = itag + 1; %skip to close
            closetag = xml( (ibra(itag)+1):(iket(itag)-1) );
            if ~strcmp(closetag, ['/' tag]), error(['<' tag '> not properly closed']); end
            %add suffix to indicate original type
            if strcmp(tag, 'integer'),
%                 keySuffix = ['__' upper(tag)];
%                 keySuffix = upper(tag);

                value = int32(value);
            end
%              keySuffix = upper(tag);   
        otherwise
            error(['Unexpected tag: ' tag])
            
    end
%     keySuffix='';
    if (dictLevel > 0 & length(key)==dictLevel), %if we have the key for current dictLevel

        if ~isempty(value) | ~isempty(valueStr),
            %construct the field name hierarchy, including advancing the array indexing
            fieldname = '';
            for ifield = 1:dictLevel,
                fieldname = [fieldname '.' key{ifield}];
                if inArray(ifield),

                        fieldname = [fieldname '{' num2str(arrayIndex(ifield)) '}'];
                end
            end

            %store value, if we have one
            if ~isempty(value), %it's numeric
                
                cmd = ['S' fieldname ' =  value ;'];
                eval(cmd);
% 
%                 cmd = ['T' fieldname ' =  keySuffix ;'];
%                 eval(cmd);

            elseif ~isempty(valueStr),  %it's string
                cmd = ['S' fieldname ' =  valueStr ;'];
%                 cmd
                eval(cmd);
                
%                 cmd = ['T' fieldname ' =  keySuffix ;'];
%                 eval(cmd);
            end
        end
        
    end
    itag = itag+1;
            
end

if (dictLevel > 0) | any(inArray),
    error('unfinished dict or array, but now at end of file')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%example plist
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% <?xml version="1.0" encoding="UTF-8"?>
% <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
% <plist version="1.0">
% <dict>
% 	<key>string</key>
% 	<string>aString</string>
% 	<key>int</key>
% 	<integer>123</integer>
% 	<key>double</key>
% 	<real>123.456</real>
% 	<key>true</key>
% 	<true/>
% 	<key>false</key>
% 	<false/>
% 	<key>stringarray</key>
% 	<array>
% 		<string>string 1</string>
% 		<string>string 2</string>
% 	</array>
% 	<key>subDict</key>
% 	<dict>
% 		<key>nodes</key>
% 		<integer>6</integer>
% 		<key>channels</key>
% 		<integer>2</integer>
% 	</dict>
% 	<key>arrayofdict</key>
% 	<array>
% 		<dict>
% 			<key>nodes</key>
% 			<integer>6</integer>
% 			<key>channels</key>
% 			<integer>2</integer>
% 		</dict>
% 		<dict>
% 			<key>nodes</key>
% 			<integer>6</integer>
% 			<key>channels</key>
% 			<integer>2</integer>
% 		</dict>
% 	</array>
% 	<key>emptyArray</key>
% 	<array/>
%   <key>aDate</key>
%   <date>2005-03-15T07:08:06Z</date>
%   <key>someData</key>
%   <data>ZDI5eWF5Qm1iM0lnY0dWaFkyVT0=</data>
% </dict>
% </plist>

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%example usage
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% >>S = XMLPlistToStruct(xmlString)
%
% S = 
% 
%          string: 'aString'
%     int__INTEGER: 123
%          double: 123.46
%            true: 1
%           false: 0
%     stringarray: {'string 1'  'string 2'}
%         subDict: [1x1 struct]
%     arrayofdict: {[1x1 struct]  [1x1 struct]}
%      emptyArray: {[]}
%      aDate__DATE: '2005-03-15T07:08:06Z'
%   someData__DATA: 'ZDI5eWF5Qm1iM0lnY0dWaFkyVT0='
% 
% >> S.arrayofdict{1}.nodes
% 
% ans =
% 
%      6
%
% >> xml = structToXMLPlist(S);

Contact us