Skip to content

Commit

Permalink
[feat] support saving dictionary to json and bjdata
Browse files Browse the repository at this point in the history
  • Loading branch information
fangq committed Mar 27, 2024
1 parent dfc744b commit e1d386d
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 18 deletions.
16 changes: 12 additions & 4 deletions jdataencode.m
Expand Up @@ -107,7 +107,7 @@
newitem = mat2jd(item, varargin{:});
elseif (ischar(item) || isa(item, 'string'))
newitem = mat2jd(item, varargin{:});
elseif (isa(item, 'containers.Map'))
elseif (isa(item, 'containers.Map') || isa(item, 'dictionary'))
newitem = map2jd(item, varargin{:});
elseif (isa(item, 'categorical'))
newitem = cell2jd(cellstr(item), varargin{:});
Expand Down Expand Up @@ -166,13 +166,21 @@
end
end
else % keep as a map and only encode its values
if (strcmp(item.KeyType, 'char'))
if (isa(item, 'dictionary'))
newitem = dictionary();
elseif (strcmp(item.KeyType, 'char'))
newitem = containers.Map();
else
newitem = containers.Map('KeyType', item.KeyType, 'ValueType', 'any');
end
for i = 1:length(names)
newitem(names{i}) = obj2jd(item(names{i}), varargin{:});
if (isa(item, 'dictionary'))
for i = 1:length(names)
newitem(names(i)) = obj2jd(item(names(i)), varargin{:});
end
else
for i = 1:length(names)
newitem(names{i}) = obj2jd(item(names{i}), varargin{:});
end
end
end

Expand Down
4 changes: 2 additions & 2 deletions loadjson.m
Expand Up @@ -282,9 +282,9 @@
mmap = filterjsonmmap(mmap, jsonopt('MMapInclude', {}, opt), 1);
mmap = cellfun(@(x) {x{1}, x{2}(1:(2 + int8(length(x{2}) >= 3 && (x{2}(3) > 0))))}, mmap, 'UniformOutput', false);
end
if (jsonopt('JDataDecode', 1, varargin{:}) == 1)
if (jsonopt('JDataDecode', 1, opt) == 1)
try
data = jdatadecode(data, 'Base64', 1, 'Recursive', 1, varargin{:});
data = jdatadecode(data, 'Base64', 1, 'Recursive', 1, opt);
catch ME
warning(['Failed to decode embedded JData annotations, '...
'return raw JSON data\n\njdatadecode error: %s\n%s\nCall stack:\n%s\n'], ...
Expand Down
28 changes: 24 additions & 4 deletions savebj.m
Expand Up @@ -316,7 +316,7 @@
end
elseif (isa(item, 'function_handle'))
txt = struct2ubjson(name, functions(item), level, varargin{:});
elseif (isa(item, 'containers.Map'))
elseif (isa(item, 'containers.Map') || isa(item, 'dictionary'))
txt = map2ubjson(name, item, level, varargin{:});
elseif (isa(item, 'categorical'))
txt = cell2ubjson(name, cellstr(item), level, varargin{:});
Expand Down Expand Up @@ -469,12 +469,28 @@
%% -------------------------------------------------------------------------
function txt = map2ubjson(name, item, level, varargin)
txt = '';
if (~isa(item, 'containers.Map'))
error('input is not a struct');
end
itemtype = isa(item, 'containers.Map');
dim = size(item);

if (isa(item, 'dictionary'))
itemtype = 2;
dim = item.numEntries;
end
if (itemtype == 0)
error('input is not a containers.Map or dictionary class');
end

names = keys(item);
val = values(item);

if (~iscell(names))
names = num2cell(names, ndims(names));
end

if (~iscell(val))
val = num2cell(val, ndims(val));
end

Omarker = varargin{1}.OM_;

if (~strcmp(Omarker{1}, '{'))
Expand All @@ -495,6 +511,9 @@
end
end
txt = [txt Omarker{2}];
if (isa(txt, 'string') && length(txt) > 1)
txt = sprintf('%s', txt);
end

%% -------------------------------------------------------------------------
function txt = str2ubjson(name, item, level, varargin)
Expand Down Expand Up @@ -841,6 +860,7 @@
%% -------------------------------------------------------------------------
function val = N_(str, varargin)
ismsgpack = varargin{1}.messagepack;
str = char(str);
if (~ismsgpack)
val = [I_(int32(length(str)), varargin{:}) str];
else
Expand Down
29 changes: 23 additions & 6 deletions savejson.m
Expand Up @@ -176,6 +176,9 @@
if (isempty(regexp(json, '^[{\[]', 'once')))
json = ['[', json, ']'];
end
if (nargout > 0)
output = json;
end
return
catch
warning('built-in jsonencode function failed to encode the data, fallback to savejson');
Expand Down Expand Up @@ -218,7 +221,7 @@
rootname = varname;
end
end
if (isa(obj, 'containers.Map') && ~strcmp(obj.KeyType, 'char'))
if ((isa(obj, 'containers.Map') && ~strcmp(obj.KeyType, 'char')) || (isa(obj, 'dictionary') && ~strcmp(obj.types, 'string')))
rootisarray = 0;
end
if ((isstruct(obj) || iscell(obj)) && isempty(rootname) && forceroot)
Expand Down Expand Up @@ -300,7 +303,7 @@
end
elseif (isa(item, 'function_handle'))
txt = struct2json(name, functions(item), level, varargin{:});
elseif (isa(item, 'containers.Map'))
elseif (isa(item, 'containers.Map') || isa(item, 'dictionary'))
txt = map2json(name, item, level, varargin{:});
elseif (isa(item, 'categorical'))
txt = cell2json(name, cellstr(item), level, varargin{:});
Expand Down Expand Up @@ -451,14 +454,28 @@
%% -------------------------------------------------------------------------
function txt = map2json(name, item, level, varargin)
txt = {};
if (~isa(item, 'containers.Map'))
error('input is not a containers.Map class');
end
itemtype = isa(item, 'containers.Map');
dim = size(item);

if (isa(item, 'dictionary'))
itemtype = 2;
dim = item.numEntries;
end
if (itemtype == 0)
error('input is not a containers.Map or dictionary class');
end
names = keys(item);
val = values(item);

if (~strcmp(item.KeyType, 'char'))
if (~iscell(names))
names = num2cell(names, ndims(names));
end

if (~iscell(val))
val = num2cell(val, ndims(val));
end

if ((itemtype == 1 && ~strcmp(item.KeyType, 'char')) || (itemtype == 2 && ~strcmp(item.types, 'string')))
mm = cell(1, length(names));
for i = 1:length(names)
mm{i} = {names{i}, val{i}};
Expand Down
20 changes: 18 additions & 2 deletions test/run_jsonlab_test.m
Expand Up @@ -101,8 +101,18 @@ function run_jsonlab_test(tests)
'{"x0x5F_i":1,"i_":"str"}', 'compact', 1, 'UnpackHex', 0);
end
if (exist('containers.Map'))
test_jsonlab('containers.Map', @savejson, containers.Map({'Andy', '^_^'}, {true, '-_-'}), ...
test_jsonlab('containers.Map with char keys', @savejson, containers.Map({'Andy', '^_^'}, {true, '-_-'}), ...
'{"Andy":true,"^_^":"-_-"}', 'compact', 1, 'usemap', 1);
test_jsonlab('containers.Map with number keys', @savejson, containers.Map({1.1, 1.2}, {true, '-_-'}), ...
'{"_MapData_":[[1.1,true],[1.2,"-_-"]]}', 'compact', 1, 'usemap', 1);
end
if (exist('dictionary'))
test_jsonlab('dictionary with string keys', @savejson, dictionary(["Andy", "^_^"], {true, '-_-'}), ...
'{"Andy":true,"^_^":"-_-"}', 'compact', 1, 'usemap', 1);
test_jsonlab('dictionary with cell keys', @savejson, dictionary({'Andy', '^_^'}, {true, '-_-'}), ...
'{"_MapData_":[["Andy",true],["^_^","-_-"]]}', 'compact', 1, 'usemap', 1);
test_jsonlab('dictionary with number keys', @savejson, dictionary({1.1, 1.2}, {true, '-_-'}), ...
'{"_MapData_":[[1.1,true],[1.2,"-_-"]]}', 'compact', 1, 'usemap', 1);
end
if (exist('istable'))
test_jsonlab('simple table', @savejson, table({'Andy', '^_^'}, {true, '-_-'}), ...
Expand Down Expand Up @@ -238,8 +248,14 @@ function run_jsonlab_test(tests)
'{U<7>x0x5F_iU<1>U<2>i_SU<3>str}', 'debug', 1, 'UnpackHex', 0);
end
if (exist('containers.Map'))
test_jsonlab('containers.Map', @savebj, containers.Map({'Andy', '^_^'}, {true, '-_-'}), ...
test_jsonlab('containers.Map with char keys', @savebj, containers.Map({'Andy', '^_^'}, {true, '-_-'}), ...
'{U<4>AndyTU<3>^_^SU<3>-_-}', 'debug', 1, 'usemap', 1);
end
if (exist('dictionary'))
test_jsonlab('dictionary with string keys', @savebj, dictionary(["Andy", "^_^"], {true, '-_-'}), ...
'{U<4>AndyTU<3>^_^SU<3>-_-}', 'debug', 1, 'usemap', 1);
test_jsonlab('dictionary with cell keys', @savebj, dictionary({'Andy', '^_^'}, {true, '-_-'}), ...
'{U<4>AndyTU<3>^_^SU<3>-_-}', 'debug', 1, 'usemap', 1);
end
if (exist('istable'))
test_jsonlab('simple table', @savebj, table({'Andy', '^_^'}, {true, '-_-'}), ...
Expand Down

0 comments on commit e1d386d

Please sign in to comment.