From 46bbfa92db3b543cba28118cbddedf44a135545d Mon Sep 17 00:00:00 2001
From: Qianqian Fang <fangqq@gmail.com>
Date: Sun, 13 Mar 2022 23:25:51 -0400
Subject: [PATCH] support empty name, which is valid in JSON, fix #79

---
 decodevarname.m         |  4 ++++
 loadbj.m                |  2 +-
 loadjson.m              |  4 ++--
 savebj.m                | 14 +++-----------
 savejson.m              | 37 +++++++++++++++----------------------
 test/run_jsonlab_test.m |  4 ++++
 6 files changed, 29 insertions(+), 36 deletions(-)

diff --git a/decodevarname.m b/decodevarname.m
index 66298d2..f1ab141 100644
--- a/decodevarname.m
+++ b/decodevarname.m
@@ -46,6 +46,10 @@ if(isunpack)
         h2u=@hex2unicode;
         newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${h2u($2)}');
     else
+        if(isunpack && strcmp(name,'x0x0_'))
+            newname='';
+            return;
+        end
         pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start');
         pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end');
         if(isempty(pos))
diff --git a/loadbj.m b/loadbj.m
index 86ffa60..79bed88 100644
--- a/loadbj.m
+++ b/loadbj.m
@@ -493,7 +493,7 @@ function [object, pos, mmap] = parse_object(inputstr, pos, varargin)
                 [str, pos] = parse_name(inputstr, pos, varargin{:});
             end
             if isempty(str)
-                error_pos('Name of value at position %d cannot be empty', inputstr, pos);
+                str='x0x0_'; % empty name is valid in BJData/UBJSON, decodevarname('x0x0_') restores '\0'
             end
             if(nargout>2)
                 varargin{1}.jsonpath_=[origpath,'.',str];
diff --git a/loadjson.m b/loadjson.m
index 1ee4c12..e6ace03 100644
--- a/loadjson.m
+++ b/loadjson.m
@@ -576,8 +576,8 @@ function [object, pos, index_esc, mmap] = parse_object(inputstr, pos, esc, index
     if cc ~= '}'
         while 1
             [str, pos, index_esc] = parseStr(inputstr, pos, esc, index_esc, varargin{:});
-            if isempty(str)
-                pos=error_pos('Name of value at position %d cannot be empty',inputstr,pos);
+            if isempty(str) && ~usemap
+                str='x0x0_'; % empty name is valid in JSON, decodevarname('x0x0_') restores '\0'
             end
             [pos, w1, w2]=parse_char(inputstr, pos, ':');
             if(nargout>3)
diff --git a/savebj.m b/savebj.m
index 28f7bb3..8493c50 100644
--- a/savebj.m
+++ b/savebj.m
@@ -477,17 +477,11 @@ if(~strcmp(Omarker{1},'{'))
 else
     om0=Omarker{1};
 end
-len=prod(dim);
-forcearray= (len>1 || (varargin{1}.singletarray==1 && level>0));
 
 if(~isempty(name)) 
-    if(forcearray)
-        txt=[N_(decodevarname(name,varargin{:}),varargin{:}) om0];
-    end
+    txt=[N_(decodevarname(name,varargin{:}),varargin{:}) om0];
 else
-    if(forcearray)
-        txt=om0;
-    end
+    txt=om0;
 end
 for i=1:dim(1)
     if(~isempty(names{i}))
@@ -495,9 +489,7 @@ for i=1:dim(1)
              level+(dim(1)>1),varargin{:})];
     end
 end
-if(forcearray)
-    txt=[txt Omarker{2}];
-end
+txt=[txt Omarker{2}];
 
 %%-------------------------------------------------------------------------
 function txt=str2ubjson(name,item,level,varargin)
diff --git a/savejson.m b/savejson.m
index 7be8352..8a21418 100644
--- a/savejson.m
+++ b/savejson.m
@@ -470,14 +470,12 @@ if(~strcmp(item.KeyType,'char'))
     return;
 end
 
-len=prod(dim);
-forcearray= (len>1 || (varargin{1}.singletarray==1 && level>0));
 ws=varargin{1}.whitespaces_;
 padding0=repmat(ws.tab,1,level);
 nl=ws.newline;
 
-if(isempty(item)) 
-    if(~isempty(name)) 
+if(isempty(item))
+    if(~isempty(name))
         txt={padding0, '"', decodevarname(name,varargin{1}.unpackhex),'":[]'};
     else
         txt={padding0, '[]'};
@@ -486,30 +484,25 @@ if(isempty(item))
     return;
 end
 if(~isempty(name)) 
-    if(forcearray)
-        txt={padding0, '"', decodevarname(name,varargin{1}.unpackhex),'":{', nl};
-    end
+    txt={padding0, '"', decodevarname(name,varargin{1}.unpackhex),'":{', nl};
 else
-    if(forcearray)
-        txt={padding0, '{', nl};
-    end
+    txt={padding0, '{', nl};
 end
 
 for i=1:dim(1)
-    if(~isempty(names{i}))
-	    txt{end+1}=obj2json(names{i},val{i},...
-             level+(dim(1)>1),varargin{:});
-        if(i<length(names))
-            txt{end+1}=',';
-        end
-        if(i<dim(1))
-            txt{end+1}=nl;
-        end
+    if(isempty(names{i}))
+        txt{end+1}=obj2json('x0x0_',val{i},level+(dim(1)>1),varargin{:});
+    else
+        txt{end+1}=obj2json(names{i},val{i},level+(dim(1)>1),varargin{:});
+    end
+    if(i<length(names))
+        txt{end+1}=',';
+    end
+    if(i<dim(1))
+        txt{end+1}=nl;
     end
 end
-if(forcearray)
-    txt(end+1:end+3)={nl,padding0,'}'};
-end
+txt(end+1:end+3)={nl,padding0,'}'};
 txt = sprintf('%s',txt{:});
 
 %%-------------------------------------------------------------------------
diff --git a/test/run_jsonlab_test.m b/test/run_jsonlab_test.m
index 11a9a97..3d9735e 100644
--- a/test/run_jsonlab_test.m
+++ b/test/run_jsonlab_test.m
@@ -49,6 +49,8 @@ if(ismember('js',tests))
         test_jsonlab('string type',@savejson,string(sprintf('jdata\n\b\ashall\tprevail')),'["jdata\n\b\ashall\tprevail"]','compact',1);
         test_jsonlab('string array',@savejson,[string('jdata'),string('shall'),string('prevail')],'["jdata","shall","prevail"]','compact',1);
     end
+    test_jsonlab('empty name',@savejson,loadjson('{"":""}'),'{"":""}','compact',1);
+    test_jsonlab('empty name with map',@savejson,loadjson('{"":""}','usemap',1),'{"":""}','compact',1);
     test_jsonlab('row vector',@savejson,[1,2,3],'[1,2,3]');
     test_jsonlab('column vector',@savejson,[1;2;3],'[[1],[2],[3]]','compact',1);
     test_jsonlab('mixed array',@savejson,{'a',1,0.9},'["a",1,0.9]','compact',1);
@@ -182,6 +184,8 @@ if(ismember('bj',tests))
         test_jsonlab('string type',@savebj,string(sprintf('jdata\n\b\ashall\tprevail')),sprintf('[SU<21>jdata\n\b\ashall\tprevail]'),'debug',1);
         test_jsonlab('string array',@savebj,[string('jdata');string('shall');string('prevail')],'[[SU<5>jdataSU<5>shallSU<7>prevail]]','debug',1);
     end
+    test_jsonlab('empty name',@savebj,loadbj(['{U' 0 'U' 2 '}']),'{U<0>U<2>}','debug',1);
+    test_jsonlab('empty name with map',@savebj,loadbj(['{U' 0 'U' 2 '}'],'usemap',1),'{U<0>U<2>}','debug',1);
     test_jsonlab('row vector',@savebj,[1,2,3],'[$U#U<3><1><2><3>','debug',1);
     test_jsonlab('column vector',@savebj,[1;2;3],'[$U#[$U#U<2><3><1><1><2><3>','debug',1);
     test_jsonlab('mixed array',@savebj,{'a',1,0.9},'[CaU<1>D<0.9>]','debug',1);
-- 
GitLab