Skip to content

Commit 874945f

Browse files
committed
decode graph data, encode non-char-keyed map data
1 parent 11712b7 commit 874945f

File tree

7 files changed

+227
-68
lines changed

7 files changed

+227
-68
lines changed

jdatadecode.m

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@
1818
% "_ArrayZipType_", "_ArrayZipSize", "_ArrayZipData_"
1919
% opt: (optional) a list of 'Param',value pairs for additional options
2020
% The supported options include
21-
% 'Recursive', if set to 1, will apply the conversion to
21+
% Recursive: [1|0] if set to 1, will apply the conversion to
2222
% every child; 0 to disable
23-
% 'Base64'. if set to 1, _ArrayZipData_ is assumed to
23+
% Base64: [0|1] if set to 1, _ArrayZipData_ is assumed to
2424
% be encoded with base64 format and need to be
2525
% decoded first. This is needed for JSON but not
2626
% UBJSON data
27-
% 'Prefix', for JData files loaded via loadjson/loadubjson, the
27+
% Prefix: ['x0x5F'|'x'] for JData files loaded via loadjson/loadubjson, the
2828
% default JData keyword prefix is 'x0x5F'; if the
2929
% json file is loaded using matlab2018's
3030
% jsondecode(), the prefix is 'x'; this function
3131
% attempts to automatically determine the prefix.
32-
% 'FormatVersion' [2|float]: set the JSONLab output version;
32+
% FormatVersion: [2|float]: set the JSONLab output version;
3333
% since v2.0, JSONLab uses JData specification Draft 1
3434
% for output format, it is incompatible with all
3535
% previous releases; if old output is desired,
@@ -51,27 +51,38 @@
5151
%
5252

5353
newdata=data;
54+
opt=varargin2struct(varargin{:});
55+
56+
%% process non-structure inputs
5457
if(~isstruct(data))
5558
if(iscell(data))
56-
newdata=cellfun(@(x) jdatadecode(x),data,'UniformOutput',false);
59+
newdata=cellfun(@(x) jdatadecode(x,opt),data,'UniformOutput',false);
60+
elseif(isa(data,'containers.Map'))
61+
newdata=containers.Map('KeyType',data.KeyType,'ValueType','any');
62+
names=data.keys;
63+
for i=1:length(names)
64+
newdata(names{i})=jdatadecode(data(names{i}),opt);
65+
end
5766
end
5867
return;
5968
end
69+
70+
%% assume the input is a struct below
6071
fn=fieldnames(data);
6172
len=length(data);
62-
opt=varargin2struct(varargin{:});
63-
needbase64=jsonopt('Base64',1,opt);
73+
needbase64=jsonopt('Base64',0,opt);
6474
format=jsonopt('FormatVersion',2,opt);
6575
prefix=jsonopt('Prefix',sprintf('x0x%X','_'+0),opt);
66-
if(isempty(strmatch(N_('_ArrayType_'),fn)) && ~isempty(strmatch('x_ArrayType_',fn)))
76+
if(~any(ismember(N_('_ArrayType_'),fn)) && any(ismember('x_ArrayType_',fn)))
6777
prefix='x';
6878
opt.prefix='x';
6979
end
7080

81+
%% recursively process subfields
7182
if(jsonopt('Recursive',1,opt)==1)
7283
for i=1:length(fn) % depth-first
7384
for j=1:len
74-
if(isstruct(data(j).(fn{i})))
85+
if(isstruct(data(j).(fn{i})) || isa(data(j).(fn{i}),'containers.Map'))
7586
newdata(j).(fn{i})=jdatadecode(data(j).(fn{i}),opt);
7687
elseif(iscell(data(j).(fn{i})))
7788
newdata(j).(fn{i})=cellfun(@(x) jdatadecode(x,opt),newdata(j).(fn{i}),'UniformOutput',false);
@@ -81,15 +92,15 @@
8192
end
8293

8394
%% handle array data
84-
if(~isempty(strmatch(N_('_ArrayType_'),fn)) && (~isempty(strmatch(N_('_ArrayData_'),fn)) || ~isempty(strmatch(N_('_ArrayZipData_'),fn))))
95+
if(any(ismember(N_('_ArrayType_'),fn)) && (any(ismember(N_('_ArrayData_'),fn)) || any(ismember(N_('_ArrayZipData_'),fn))))
8596
newdata=cell(len,1);
8697
for j=1:len
87-
if(~isempty(strmatch(N_('_ArrayZipSize_'),fn)) && ~isempty(strmatch(N_('_ArrayZipData_'),fn)))
98+
if(any(ismember(N_('_ArrayZipSize_'),fn)) && any(ismember(N_('_ArrayZipData_'),fn)))
8899
zipmethod='zip';
89-
if(~isempty(strmatch(N_('_ArrayZipType_'),fn)))
100+
if(any(ismember(N_('_ArrayZipType_'),fn)))
90101
zipmethod=data(j).(N_('_ArrayZipType_'));
91102
end
92-
if(~isempty(strmatch(zipmethod,{'zlib','gzip','lzma','lzip','lz4','lz4hc'})))
103+
if(any(ismember(zipmethod,{'zlib','gzip','lzma','lzip','lz4','lz4hc'})))
93104
decompfun=str2func([zipmethod 'decode']);
94105
if(needbase64)
95106
ndata=reshape(typecast(decompfun(base64decode(data(j).(N_('_ArrayZipData_')))),data(j).(N_('_ArrayType_'))),data(j).(N_('_ArrayZipSize_'))(:)');
@@ -105,18 +116,18 @@
105116
end
106117
ndata=cast(data(j).(N_('_ArrayData_')),char(data(j).(N_('_ArrayType_'))));
107118
end
108-
if(~isempty(strmatch(N_('_ArrayZipSize_'),fn)))
119+
if(any(ismember(N_('_ArrayZipSize_'),fn)))
109120
ndata=reshape(ndata(:),fliplr(data(j).(N_('_ArrayZipSize_'))(:)'));
110121
ndata=permute(ndata,ndims(ndata):-1:1);
111122
end
112123
iscpx=0;
113-
if(~isempty(strmatch(N_('_ArrayIsComplex_'),fn)))
124+
if(any(ismember(N_('_ArrayIsComplex_'),fn)))
114125
if(data(j).(N_('_ArrayIsComplex_')))
115126
iscpx=1;
116127
end
117128
end
118-
if(~isempty(strmatch(N_('_ArrayIsSparse_'),fn)) && data(j).(N_('_ArrayIsSparse_')))
119-
if(~isempty(strmatch(N_('_ArraySize_'),fn)))
129+
if(any(ismember(N_('_ArrayIsSparse_'),fn)) && data(j).(N_('_ArrayIsSparse_')))
130+
if(any(ismember(N_('_ArraySize_'),fn)))
120131
dim=double(data(j).(N_('_ArraySize_'))(:)');
121132
if(iscpx)
122133
ndata(end-1,:)=complex(ndata(end-1,:),ndata(end,:));
@@ -140,7 +151,7 @@
140151
end
141152
ndata=sparse(ndata(1,:),ndata(2,:),ndata(3,:));
142153
end
143-
elseif(~isempty(strmatch(N_('_ArraySize_'),fn)))
154+
elseif(any(ismember(N_('_ArraySize_'),fn)))
144155
if(iscpx)
145156
ndata=complex(ndata(1,:),ndata(2,:));
146157
end
@@ -161,7 +172,7 @@
161172
end
162173

163174
%% handle table data
164-
if(~isempty(strmatch(N_('_TableRecords_'),fn)))
175+
if(any(ismember(N_('_TableRecords_'),fn)))
165176
newdata=cell(len,1);
166177
for j=1:len
167178
ndata=data(j).(N_('_TableRecords_'));
@@ -191,14 +202,14 @@
191202
end
192203

193204
%% handle map data
194-
if(~isempty(strmatch(N_('_MapData_'),fn)))
205+
if(any(ismember(N_('_MapData_'),fn)))
195206
newdata=cell(len,1);
196207
for j=1:len
197-
key={};
198-
val={};
208+
key=cell(1,length(data(j).(N_('_MapData_'))));
209+
val=cell(size(key));
199210
for k=1:length(data(j).(N_('_MapData_')))
200211
key{k}=data(j).(N_('_MapData_')){k}{1};
201-
val{k}=data(j).(N_('_MapData_')){k}{2};
212+
val{k}=jdatadecode(data(j).(N_('_MapData_')){k}{2},opt);
202213
end
203214
ndata=containers.Map(key,val);
204215
newdata{j}=ndata;
@@ -207,6 +218,74 @@
207218
newdata=newdata{1};
208219
end
209220
end
221+
222+
%% handle graph data
223+
if(any(ismember(N_('_GraphNodes_'),fn)) && exist('graph','file') && exist('digraph','file'))
224+
newdata=cell(len,1);
225+
isdirected=1;
226+
for j=1:len
227+
nodedata=data(j).(N_('_GraphNodes_'));
228+
if(isstruct(nodedata))
229+
nodetable=struct2table(nodedata);
230+
elseif(isa(nodedata,'containers.Map'))
231+
nodetable=[keys(nodedata);values(nodedata)];
232+
if(strcmp(nodedata.KeyType,'char'))
233+
nodetable=table(nodetable(1,:)',nodetable(2,:)','VariableNames',{'Name','Data'});
234+
else
235+
nodetable=table(nodetable(2,:)','VariableNames',{'Data'});
236+
end
237+
else
238+
nodetable=table;
239+
end
240+
241+
if(any(ismember(N_('_GraphEdges_'),fn)))
242+
edgedata=data(j).(N_('_GraphEdges_'));
243+
elseif(any(ismember(N_('_GraphEdges0_'),fn)))
244+
edgedata=data(j).(N_('_GraphEdges0_'));
245+
isdirected=0;
246+
elseif(any(ismember(N_('_GraphMatrix_'),fn)))
247+
edgedata=jdatadecode(data(j).(N_('_GraphMatrix_')),varargin{:});
248+
end
249+
250+
if(exist('edgedata','var'))
251+
if(iscell(edgedata))
252+
endnodes=edgedata(:,1:2);
253+
endnodes=reshape([endnodes{:}],size(edgedata,1),2);
254+
weight=cell2mat(edgedata(:,3:end));
255+
edgetable=table(endnodes,[weight.Weight]','VariableNames',{'EndNodes','Weight'});
256+
257+
if(isdirected)
258+
newdata{j}=digraph(edgetable,nodetable);
259+
else
260+
newdata{j}=graph(edgetable,nodetable);
261+
end
262+
elseif(ismatrix(edgedata) && isstruct(nodetable))
263+
newdata{j}=digraph(edgedata,fieldnames(nodetable));
264+
end
265+
end
266+
end
267+
if(len==1)
268+
newdata=newdata{1};
269+
end
270+
end
271+
272+
%% handle bytestream and arbitrary matlab objects
273+
if(sum(ismember({N_('_ByteStream_'),N_('_DataInfo_')},fn))==2)
274+
newdata=cell(len,1);
275+
for j=1:len
276+
if(isfield(data(j).(N_('_DataInfo_')),'MATLABObjectClass'))
277+
if(needbase64)
278+
newdata{j}=getArrayFromByteStream(base64decode(data(j).(N_('_ByteStream_'))));
279+
else
280+
newdata{j}=getArrayFromByteStream(data(j).(N_('_ByteStream_')));
281+
end
282+
end
283+
end
284+
if(len==1)
285+
newdata=newdata{1};
286+
end
287+
end
288+
210289
%% subfunctions
211290
function escaped=N_(str)
212291
escaped=[prefix str];

jdataencode.m

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@
5050
end
5151

5252
opt=varargin2struct(varargin{:});
53-
opt.prefix=jsonopt('Prefix',sprintf('x0x%X','_'+0),opt);
54-
5553
jdata=obj2jd(data,opt);
5654

5755
%%-------------------------------------------------------------------------
@@ -61,6 +59,8 @@
6159
newitem=cell2jd(item,varargin{:});
6260
elseif(isstruct(item))
6361
newitem=struct2jd(item,varargin{:});
62+
elseif(isnumeric(item) || islogical(item))
63+
newitem=mat2jd(item,varargin{:});
6464
elseif(ischar(item) || isa(item,'string'))
6565
newitem=mat2jd(item,varargin{:});
6666
elseif(isa(item,'containers.Map'))
@@ -69,14 +69,14 @@
6969
newitem=cell2jd(cellstr(item),varargin{:});
7070
elseif(isa(item,'function_handle'))
7171
newitem=struct2jd(functions(item),varargin{:});
72-
elseif(islogical(item) || isnumeric(item))
73-
newitem=mat2jd(item,varargin{:});
7472
elseif(isa(item,'table'))
7573
newitem=table2jd(item,varargin{:});
7674
elseif(isa(item,'digraph') || isa(item,'graph'))
7775
newitem=graph2jd(item,varargin{:});
78-
else
76+
elseif(~isoctavemesh)
7977
newitem=any2jd(item,varargin{:});
78+
else
79+
newitem=item;
8080
end
8181

8282
%%-------------------------------------------------------------------------
@@ -88,13 +88,13 @@
8888
function newitem=struct2jd(item,varargin)
8989

9090
num=numel(item);
91-
if(num>1)
91+
if(num>1) % struct array
9292
newitem=obj2jd(num2cell(item),varargin{:});
9393
try
9494
newitem=cell2mat(newitem);
9595
catch
9696
end
97-
else
97+
else % a single struct
9898
names=fieldnames(item);
9999
newitem=struct;
100100
for i=1:length(names)
@@ -106,19 +106,28 @@
106106
function newitem=map2jd(item,varargin)
107107

108108
names=item.keys;
109-
if(jsonopt('MapAsStruct',0,varargin{:}))
109+
if(jsonopt('MapAsStruct',0,varargin{:})) % convert a map to struct
110110
newitem=struct;
111-
for i=1:length(names)
112-
newitem(N_(names{i},varargin{:}))=obj2jd(item(names{i}),varargin{:});
111+
if(~strcmp(item.KeyType,'char'))
112+
data=num2cell(reshape([names, item.values],length(names),2),2);
113+
for i=1:length(names)
114+
data{i}{2}=obj2jd(data{i}{2},varargin{:});
115+
end
116+
newitem.(N_('_MapData_',varargin{:}))=data;
117+
else
118+
for i=1:length(names)
119+
newitem.(N_(names{i},varargin{:}))=obj2jd(item(names{i}),varargin{:});
120+
end
113121
end
114-
else
115-
newitem=containers.Map;
122+
else % keep as a map and only encode its values
123+
newitem=containers.Map('KeyType',item.KeyType,'ValueType','any');
116124
for i=1:length(names)
117125
newitem(names{i})=obj2jd(item(names{i}),varargin{:});
118126
end
119127
end
120128
%%-------------------------------------------------------------------------
121129
function newitem=mat2jd(item,varargin)
130+
122131
if(isempty(item) || isa(item,'string') || ischar(item) || (isvector(item) && isreal(item) && ~issparse(item)))
123132
newitem=item;
124133
return;
@@ -173,6 +182,7 @@
173182
data=reshape(data,fliplr(newitem.(N('_ArrayZipSize_'))));
174183
newitem.(N('_ArrayData_'))=permute(data,ndims(data):-1:1);
175184
end
185+
176186
if(~isempty(zipmethod) && numel(item)>minsize)
177187
compfun=str2func([zipmethod 'encode']);
178188
newitem.(N('_ArrayZipType_'))=lower(zipmethod);
@@ -186,35 +196,51 @@
186196

187197
%%-------------------------------------------------------------------------
188198
function newitem=table2jd(item,varargin)
199+
189200
newitem=struct;
190-
newitem(N('_TableRows_',varargin{:}))=item.Properties.RowNames';
191-
newitem(N('_TableCols_',varargin{:}))=item.Properties.VariableNames;
192-
newitem(N('_TableRecords_',varargin{:}))=table2cell(item);
201+
newitem(N_('_TableRows_',varargin{:}))=item.Properties.RowNames';
202+
newitem(N_('_TableCols_',varargin{:}))=item.Properties.VariableNames;
203+
newitem(N_('_TableRecords_',varargin{:}))=table2cell(item);
193204

194205
%%-------------------------------------------------------------------------
195206
function newitem=graph2jd(item,varargin)
207+
196208
newitem=struct;
197209
nodedata=table2struct(item.Nodes);
198210
if(isfield(nodedata,'Name'))
199211
nodedata=rmfield(nodedata,'Name');
200-
newitem.(N_('_GraphNodes_',varargin{:}))=containers.Map(item.Nodes.Name,num2cell(nodedata),'uniformValues',false);
212+
newitem.(N_('_GraphNodes_',varargin{:}))=containers.Map(item.Nodes.Name,num2cell(nodedata),'UniformValues',false);
201213
else
202-
newitem.(N_('_GraphNodes_',varargin{:}))=containers.Map(1:max(item.Edges.EndNodes(:)),num2cell(nodedata),'uniformValues',false);
214+
newitem.(N_('_GraphNodes_',varargin{:}))=containers.Map(1:max(item.Edges.EndNodes(:)),num2cell(nodedata),'UniformValues',false);
203215
end
204-
edgenodes=item.Edges.EndNodes;
216+
edgenodes=num2cell(item.Edges.EndNodes);
205217
edgedata=table2struct(item.Edges);
206218
if(isfield(edgedata,'EndNodes'))
207219
edgedata=rmfield(edgedata,'EndNodes');
208220
end
209221
edgenodes(:,3)=num2cell(edgedata);
210-
newitem.(N_('_GraphEdges_',varargin{:}))=edgenodes;
222+
if(isa(item,'graph'))
223+
if(strcmp(jsonopt('Prefix',sprintf('x0x%X','_'+0),varargin{:}),'x'))
224+
newitem.(genvarname('_GraphEdges0_'))=edgenodes;
225+
else
226+
newitem.(encodevarname('_GraphEdges0_'))=edgenodes;
227+
end
228+
else
229+
newitem.(N_('_GraphEdges_',varargin{:}))=edgenodes;
230+
end
211231

212232
%%-------------------------------------------------------------------------
213233
function newitem=any2jd(item,varargin)
214-
newitem.(N_('_DataInfo_',varargin{:}))=struct('MATLABObjectClass',class(item),'MATLABObjectSize',size(item));
215-
newitem.(N_('_ByteStream_',varargin{:}))=getByteStreamFromArray(item); % use undocumented matlab function
234+
235+
N=@(x) N_(x,varargin{:});
236+
newitem.(N('_DataInfo_'))=struct('MATLABObjectClass',class(item),'MATLABObjectSize',size(item));
237+
newitem.(N('_ByteStream_'))=getByteStreamFromArray(item); % use undocumented matlab function
238+
if(jsonopt('Base64',0,varargin{:}))
239+
newitem.(N('_ByteStream_'))=char(base64encode(newitem.(N('_ByteStream_'))));
240+
end
216241

217242
%%-------------------------------------------------------------------------
218243
function newname=N_(name,varargin)
244+
219245
prefix=jsonopt('Prefix',sprintf('x0x%X','_'+0),varargin{:});
220246
newname=[prefix name];

loadjson.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@
134134
end
135135
end
136136

137-
%% all functions
137+
%%-------------------------------------------------------------------------
138+
%% helper functions
139+
%%-------------------------------------------------------------------------
138140

139141
function [object, pos,index_esc] = parse_array(inputstr, pos, esc, index_esc, varargin) % JSON array is written in row-major order
140142
pos=parse_char(inputstr, pos, '[');
@@ -424,7 +426,7 @@
424426
pos=parse_char(inputstr, pos, '}');
425427
if(isstruct(object) && jsonopt('JDataDecode',1,varargin{:})==1)
426428
varargin{:}.Recursive=0;
427-
object=jdatadecode(object,varargin{:});
429+
object=jdatadecode(object,'Base64',1,varargin{:});
428430
end
429431
end
430432

0 commit comments

Comments
 (0)