Skip to content

Commit 7804e8d

Browse files
committed
[jdict] add isKey(), update help info
1 parent e350a5a commit 7804e8d

1 file changed

Lines changed: 59 additions & 23 deletions

File tree

jdict.m

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,28 @@
1313
% string starting with http:// or https://, loadjson(data)
1414
% will be used to dynamically load the data
1515
%
16+
% constructors:
17+
% jd = jdict creates an empty jdict object (like an empty struct or containers.Map)
18+
% jd = jdict(data) wraps any matlab data (array, cell, struct, dictionary, ...) into a new jdict object
19+
% jd = jdict(data, 'param1', value1, 'param2', value2, ...) use param/value pairs to initilize jd.flags
20+
% jd = jdict(data, 'attr', attrmap) initilize data attributes using a containers.Map with JSONPath as keys
21+
%
22+
% member functions:
23+
% jd.('cell1').v(i) or jd.('array1').v(2:3) returns specified elements if the element is a cell or array
24+
% jd.('key1').('subkey1').v() returns the underlying hierachical data at the specified subkeys
25+
% jd.tojson() convers the underlying data to a JSON string
26+
% jd.tojson('compression', 'zlib', ...) convers the data to a JSON string with savejson() options
27+
% jd.keys() returns the sub-key names of the object - if it a struct, dictionary or containers.Map - or 1:length(data) if it is an array
28+
% jd.len() returns the length of the sub-keys
29+
% jd.size() returns the dimension vector
30+
% jd.isKey(key) tests if a string-based key exists in the data, or number-based key is within the data array length
31+
% jd{'attrname'} gets/sets attributes using curly bracket indexing; jd{'attrname'}=val only works in MATLAB; use setattr() in octave
32+
% jd.setattr(jsonpath, attrname, value) sets attribute at any path
33+
% jd.getattr(jsonpath, attrname) gets attribute from any path
34+
%
35+
% if using matlab, the .v(...) method can be replaced by bare
36+
% brackets .(...), but in octave, one must use .v(...)
37+
%
1638
% indexing:
1739
% jd.('key1').('subkey1')... can retrieve values that are recursively index keys
1840
% jd.key1.subkey1... can also retrieve the same data regardless
@@ -24,22 +46,6 @@
2446
% jd.('$.key1.subkey1[0].subsubkey1') JSONPath can also apply further indexing over objects of diverse types
2547
% jd.('$.key1..subkey') JSONPath can use '..' deep-search operator to find and retrieve subkey appearing at any level below
2648
%
27-
% member functions:
28-
% jd() or jd.v() returns the underlying hierachical data
29-
% jd.('cell1').v(i) or jd.('array1').v(2:3) returns specified elements if the element is a cell or array
30-
% jd.('key1').('subkey1').v() returns the underlying hierachical data at the specified subkeys
31-
% jd.tojson() convers the underlying data to a JSON string
32-
% jd.tojson('compression', 'zlib', ...) convers the data to a JSON string with savejson() options
33-
% jd.keys() return the sub-key names of the object - if it a struct, dictionary or containers.Map - or 1:length(data) if it is an array
34-
% jd.len() return the length of the sub-keys
35-
% jd.size() return the dimension vector
36-
% jd{'attrname'} get/set attributes using curly brace indexing
37-
% jd.setattr(jsonpath, attrname, value) set attribute at any path
38-
% jd.getattr(jsonpath, attrname) get attribute from any path
39-
%
40-
% if using matlab, the .v(...) method can be replaced by bare
41-
% brackets .(...), but in octave, one must use .v(...)
42-
%
4349
% examples:
4450
%
4551
% jd = jdict;
@@ -101,15 +107,16 @@
101107

102108
classdef jdict < handle
103109
properties
104-
data
105-
attr
110+
data % underlying data: any matlab data (array, struct, cell, containers.Map, dictionary etc), retrieve via .v()
111+
attr % data attributes, stored via a containers.Map with JSONPath-based keys, retrieve via .getattr() or {}
106112
end
107113
properties (Access = private)
108-
flags
109-
currentpath
114+
flags % additional options, will be passed to jsonlab utility functions such as savejson/loadjson
115+
currentpath % internal variable tracking the current path when lookup embedded data at current depth
110116
end
111117
methods
112118

119+
% constructor: initialize a jdict object
113120
function obj = jdict(val, varargin)
114121
obj.flags = struct('builtinjson', 0);
115122
obj.attr = containers.Map();
@@ -118,6 +125,9 @@
118125
if (~isempty(varargin))
119126
allflags = [varargin(1:2:end); varargin(2:2:end)];
120127
obj.flags = struct(allflags{:});
128+
if (isfield(obj.flags, 'attr'))
129+
obj.attr = obj.flags.attr;
130+
end
121131
end
122132
if (ischar(val) && ~isempty(regexpi(val, '^https*://', 'once')))
123133
try
@@ -138,6 +148,7 @@
138148
end
139149
end
140150

151+
% overloaded indexing operator: handling assignments at arbitrary depths
141152
function varargout = subsref(obj, idxkey)
142153
% overloading the getter function jd.('key').('subkey')
143154

@@ -176,7 +187,7 @@
176187

177188
if (strcmp(idx.type, '.') && isnumeric(idx.subs))
178189
val = val(idx.subs);
179-
elseif ((strcmp(idx.type, '()') || strcmp(idx.type, '.')) && ischar(idx.subs) && ismember(idx.subs, {'tojson', 'fromjson', 'v', 'keys', 'len', 'size', 'setattr', 'getattr'}) && i < oplen)
190+
elseif ((strcmp(idx.type, '()') || strcmp(idx.type, '.')) && ischar(idx.subs) && ismember(idx.subs, {'tojson', 'fromjson', 'v', 'isKey', 'keys', 'len', 'size', 'setattr', 'getattr'}) && i < oplen)
180191
if (strcmp(idx.subs, 'v'))
181192
if (iscell(val) && strcmp(idxkey(i + 1).type, '()'))
182193
idxkey(i + 1).type = '{}';
@@ -195,6 +206,10 @@
195206
tempobj.attr = obj.attr;
196207
tempobj.currentpath = trackpath;
197208
val = fhandle(tempobj, idxkey(i + 1).subs{:});
209+
if (i == oplen - 1 && strcmp(idx.subs, 'isKey'))
210+
varargout{1} = val;
211+
return
212+
end
198213
end
199214
i = i + 1;
200215
if (i < oplen)
@@ -296,11 +311,12 @@
296311
varargout{1} = val;
297312
end
298313

314+
% overloaded assignment operator: handling assignments at arbitrary depths
299315
function obj = subsasgn(obj, idxkey, otherobj)
300316
% overloading the setter function, obj.('key').('subkey')=otherobj
301317
% expanded from rahnema1's sample at https://stackoverflow.com/a/79030223/4271392
302318

303-
% handle curly brace indexing for setting attributes
319+
% handle curly bracket indexing for setting attributes
304320
oplen = length(idxkey);
305321
if (oplen == 1 && strcmp(idxkey(1).type, '{}'))
306322
if (iscell(idxkey(1).subs) && ~isempty(idxkey(1).subs))
@@ -475,11 +491,13 @@
475491
obj.data = opcell{1};
476492
end
477493

494+
% export data to json
478495
function val = tojson(obj, varargin)
479496
% printing underlying data to compact-formed JSON string
480497
val = obj.call_('savejson', '', obj.data, 'compact', 1, varargin{:});
481498
end
482499

500+
% load data from over a dozen data formats, including json and binary json
483501
function obj = fromjson(obj, fname, varargin)
484502
% loading diverse data files using loadjd interface in jsonlab
485503
obj.data = obj.call_('loadjd', fname, varargin{:});
@@ -496,6 +514,19 @@
496514
end
497515
end
498516

517+
% test if a key or index exists
518+
function val = isKey(obj, key)
519+
% list subfields at the current level
520+
if (isstruct(obj.data))
521+
val = isfield(obj.data, key);
522+
elseif (isa(obj.data, 'containers.Map') || isa(obj.data, 'dictionary'))
523+
val = isKey(obj.data, key);
524+
else
525+
val = (key < length(obj.data));
526+
end
527+
end
528+
529+
% return the number of subfields or array length
499530
function val = len(obj)
500531
% return the number of subfields at the current level
501532
if (isstruct(obj.data))
@@ -505,11 +536,13 @@
505536
end
506537
end
507538

539+
% return the dimension vector
508540
function val = size(obj)
509541
% return the dimension vector of the data
510542
val = size(obj.data);
511543
end
512544

545+
% return the enclosed data
513546
function val = v(obj, varargin)
514547
if (~isempty(varargin))
515548
val = subsref(obj.data, varargin{:});
@@ -518,15 +551,16 @@
518551
end
519552
end
520553

554+
% internal: insert new key if does not exist
521555
function val = newkey_(obj)
522-
% insert new key if does not exist
523556
if (exist('containers.Map'))
524557
val = containers.Map;
525558
else
526559
val = struct;
527560
end
528561
end
529562

563+
% internal: call member functions or external functions
530564
function varargout = call_(obj, func, varargin)
531565
% interface to external functions and dependencies
532566
if (~obj.flags.builtinjson)
@@ -550,6 +584,7 @@
550584
end
551585
end
552586

587+
% set specified data attributes
553588
function attr = setattr(obj, jsonpath, attrname, attrvalue)
554589
if (nargin == 3)
555590
attrvalue = attrname;
@@ -565,6 +600,7 @@
565600
attr = obj.attr;
566601
end
567602

603+
% return specified data attributes, if not specified, list all attributes
568604
function val = getattr(obj, jsonpath, attrname)
569605
if (nargin == 1)
570606
if (isKey(obj.attr, obj.currentpath))

0 commit comments

Comments
 (0)