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

XMLPlistToStruct2(xml)
function [S KT] = XMLPlistToStruct2(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;

expr='';
KT{1,2} = '';
usedname={{}};
arraylv=0;
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',
            
            expr=[expr 'struct('];
            
            dictLevel = dictLevel + 1;
%             inArray(dictLevel) = 0;
            arraylv(dictLevel)=0;
            usedname{dictLevel}={};
        case '/dict',
            expr=[expr(1:end-1) '),']; % delete ","

            
%             key(dictLevel) = [];
%             if (inArray(dictLevel) ~= 0), error('Array not closed by end of dict'); end
             dictLevel = dictLevel - 1;
%             usedname(dictLevel) = {};
        case 'key',
            keyname = xml( (iket(itag)+1):(ibra(itag+1)-1) );
            fieldname = genvarname(keyname,usedname{dictLevel});% generate valid variable name.
%             key{dictLevel} = genvarname(keyname,KT(:,2));% generate valid variable name.
            usedname{dictLevel}{end+1} = fieldname;
            KT{ikey,1} = keyname;
            KT{ikey,2} = fieldname;

%            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
            expr = [expr '''' fieldname ''',' ];
            
        case 'array',
%             
            if arraylv(dictLevel) ==0
                expr = [expr '{{'];
            else
                expr = [expr '{'];
            end
%             expr = [expr '{'];            
            arraylv(dictLevel) = arraylv(dictLevel) + 1;
%             inArray(dictLevel) = inArray(dictLevel) +1;
%             arrayIndex(dictLevel) = 0;
%             arraystr = [arraystr '{'];
            
        case '/array',
            arraylv(dictLevel) = arraylv(dictLevel) - 1;
            if arraylv(dictLevel) ==0
                expr = [expr '}},'];
            else
                expr = [expr '},'];
            end
            

%             inArray(dictLevel) = inArray(dictLevel) +1;
%             arrayIndex(dictLevel) = 0;
%             arraystr = [arraystr '}'];

        case 'array/', %empty array
            expr = [expr '{{[]}},'];

%             value = { [] };
                
        case 'true/'
            
           expr = [expr 'true,'];

%             value = true; %logical

        case 'false/'
            expr = [expr 'false,'];

%             value = false; %logical

        case {'string', 'date', 'data'},
            valueStr = xml( (iket(itag)+1):(ibra(itag+1)-1) );
            

            
            str = sprintf('c%s(''%s''),',upper(tag), valueStr);
            expr = [expr str];
%           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);
                str =sprintf('int32(%d),',value);
            else
                str =sprintf('%.100g,',value);
            end
%              keySuffix = upper(tag);   
            expr = [expr str];
        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

expr=regexprep(expr,char(10),'');
expr=regexprep(expr,char(13),'');
expr=regexprep(expr,'\t','');
% fid  = fopen('creatS.m','w');
% fprintf(fid,'S = %s',expr);

fclose(fid);
S = eval(expr);
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