diff --git a/ChangeLog.txt b/ChangeLog.txt
index bfc028fdb2a19acec2c6150e4b0032eb87bc9181..853734c23806a402872adf108d92931f114c990d 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -6,10 +6,17 @@
 
 JSONlab ChangeLog (key features marked by *):
 
+== JSONlab 0.9.0 (codename: Rodimus), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
+
+ 2012/06/17 *new format for an invalid leading char, unpacking hex code in savejson
+ 2012/06/01  support JSONP in savejson
+ 2012/05/25  fix the empty cell bug (reported by Cyril Davin)
+ 2012/04/05  savejson can save to a file (suggested by Patrick Rapin)
+
 == JSONlab 0.8.1 (codename: Sentiel, Update 1), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
 
- 2012/02/28 loadjson quotation mark escape bug, see http://bit.ly/yyk1nS
- 2012/01/25 patch to handle root-less objects, contributed by Blake Johnson
+ 2012/02/28  loadjson quotation mark escape bug, see http://bit.ly/yyk1nS
+ 2012/01/25  patch to handle root-less objects, contributed by Blake Johnson
 
 == JSONlab 0.8.0 (codename: Sentiel), FangQ <fangq (at) nmr.mgh.harvard.edu> ==
 
diff --git a/examples/jsonlab_basictest.matlab b/examples/jsonlab_basictest.matlab
index 97f60fd82c907ca6b82aac6dae2ae50336634f78..ff8380a1d2447054a939b40df143c61c632511a4 100644
--- a/examples/jsonlab_basictest.matlab
+++ b/examples/jsonlab_basictest.matlab
@@ -353,9 +353,9 @@ json2data =
 >> >> 
 json2data = 
 
-                ValidName: 1
-            x_InvalidName: 2
-       x_0x3A_Field_0x3A_: 3
-    x_0xE9A1B9__0xE79BAE_: '绝密'
+               ValidName: 1
+       x0x5F_InvalidName: 2
+       x0x3A_Field_0x3A_: 3
+    x0xE9A1B9__0xE79BAE_: '绝密'
 
 >> >> >> >> 
\ No newline at end of file
diff --git a/loadjson.m b/loadjson.m
index e4f79dec1ec4eeda7cb816fba99d60d04c1088f9..480b33d818e69b7c11518e7445f130a033dedd7d 100644
--- a/loadjson.m
+++ b/loadjson.m
@@ -121,33 +121,33 @@ for i=1:length(fn) % depth-first
         end
     end
 end
-if(~isempty(strmatch('x_ArrayType_',fn)) && ~isempty(strmatch('x_ArrayData_',fn)))
+if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn)))
   newdata=cell(len,1);
   for j=1:len
-    ndata=cast(data(j).x_ArrayData_,data(j).x_ArrayType_);
+    ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_);
     iscpx=0;
-    if(~isempty(strmatch('x_ArrayIsComplex_',fn)))
-        if(data(j).x_ArrayIsComplex_)
+    if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn)))
+        if(data(j).x0x5F_ArrayIsComplex_)
            iscpx=1;
         end
     end
-    if(~isempty(strmatch('x_ArrayIsSparse_',fn)))
-        if(data(j).x_ArrayIsSparse_)
+    if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn)))
+        if(data(j).x0x5F_ArrayIsSparse_)
             if(iscpx && size(ndata,2)==4)
                 ndata(:,3)=complex(ndata(:,3),ndata(:,4));
             end
-            if(~isempty(strmatch('x_ArraySize_',fn)))
-                dim=data(j).x_ArraySize_;
+            if(~isempty(strmatch('x0x5F_ArraySize_',fn)))
+                dim=data(j).x0x5F_ArraySize_;
                 ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end)));
             else
                 ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3));
             end
         end
-    elseif(~isempty(strmatch('x_ArraySize_',fn)))
+    elseif(~isempty(strmatch('x0x5F_ArraySize_',fn)))
         if(iscpx && size(ndata,2)==2)
              ndata=complex(ndata(:,1),ndata(:,2));
         end
-        ndata=reshape(ndata(:),data(j).x_ArraySize_);
+        ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_);
     end
     newdata{j}=ndata;
   end
@@ -421,9 +421,14 @@ global isoct
 % From MATLAB doc: field names must begin with a letter, which may be
 % followed by any combination of letters, digits, and underscores.
 % Invalid characters will be converted to underscores, and the prefix
-% "x_" will be added if first character is not a letter.
-    if ~isletter(str(1)) || str(1)>'z'
-        str = ['x' str];
+% "x0x[Hex code]_" will be added if the first character is not a letter.
+    pos=regexp(str,'^[^A-Za-z]','once');
+    if(~isempty(pos))
+        if(~isoct)
+            str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once');
+        else
+            str=sprintf('x0x%X_%s',char(str(1)),str(2:end));
+        end
     end
     if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return;  end
     if(~isoct)
diff --git a/savejson.m b/savejson.m
index da6431d3aca4abd808f353fc51ec4bd904aedb5b..27995b743f4a6cc5cd0ddfe8b9fdb3a0bdc6f893 100644
--- a/savejson.m
+++ b/savejson.m
@@ -57,6 +57,8 @@ function json=savejson(rootname,obj,varargin)
 %        opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
 %                         for example, if opt.JSON='foo', the JSON data is
 %                         wrapped inside a function call as 'foo(...);'
+%        opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson 
+%                         back to the string form
 %        opt can be replaced by a list of ('param',value) pairs. The param 
 %        string is equivallent to a field in opt.
 % output:
@@ -80,6 +82,7 @@ if(length(varargin)==1 && ischar(varargin{1}))
 else
    opt=varargin2struct(varargin{:});
 end
+opt.IsOctave=exist('OCTAVE_VERSION');
 rootisarray=0;
 rootlevel=1;
 forceroot=jsonopt('ForceRootName',0,opt);
@@ -139,13 +142,13 @@ padding1=repmat(sprintf('\t'),1,level-1);
 padding0=repmat(sprintf('\t'),1,level);
 if(len>1) 
     if(~isempty(name))
-        txt=sprintf('%s"%s": [\n',padding0, name); name=''; 
+        txt=sprintf('%s"%s": [\n',padding0, checkname(name,varargin{:})); name=''; 
     else
         txt=sprintf('%s[\n',padding0); 
     end
 elseif(len==0)
     if(~isempty(name))
-        txt=sprintf('%s"%s": null',padding0, name); name=''; 
+        txt=sprintf('%s"%s": null',padding0, checkname(name,varargin{:})); name=''; 
     else
         txt=sprintf('%snull',padding0); 
     end
@@ -168,14 +171,14 @@ padding0=repmat(sprintf('\t'),1,level);
 sep=',';
 
 if(~isempty(name)) 
-    if(len>1) txt=sprintf('%s"%s": [\n',padding0,name); end
+    if(len>1) txt=sprintf('%s"%s": [\n',padding0,checkname(name,varargin{:})); end
 else
     if(len>1) txt=sprintf('%s[\n',padding0); end
 end
 for e=1:len
   names = fieldnames(item(e));
   if(~isempty(name) && len==1)
-        txt=sprintf('%s%s"%s": {\n',txt,repmat(sprintf('\t'),1,level+(len>1)), name); 
+        txt=sprintf('%s%s"%s": {\n',txt,repmat(sprintf('\t'),1,level+(len>1)), checkname(name,varargin{:})); 
   else
         txt=sprintf('%s%s{\n',txt,repmat(sprintf('\t'),1,level+(len>1))); 
   end
@@ -207,11 +210,11 @@ padding1=repmat(sprintf('\t'),1,level);
 padding0=repmat(sprintf('\t'),1,level+1);
 
 if(~isempty(name)) 
-    if(len>1) txt=sprintf('%s"%s": [\n',padding1,name); end
+    if(len>1) txt=sprintf('%s"%s": [\n',padding1,checkname(name,varargin{:})); end
 else
     if(len>1) txt=sprintf('%s[\n',padding1); end
 end
-isoct=exist('OCTAVE_VERSION');
+isoct=jsonopt('IsOctave',0,varargin{:});
 for e=1:len
     if(isoct)
         val=regexprep(item(e,:),'\\','\\');
@@ -223,7 +226,7 @@ for e=1:len
         val=regexprep(val,'^"','\\"');
     end
     if(len==1)
-        obj=['"' name '": ' '"',val,'"'];
+        obj=['"' checkname(name,varargin{:}) '": ' '"',val,'"'];
 	if(isempty(name)) obj=['"',val,'"']; end
         txt=sprintf('%s%s%s%s',txt,repmat(sprintf('\t'),1,level),obj);
     else
@@ -249,7 +252,7 @@ if(length(size(item))>2 || issparse(item) || ~isreal(item) || jsonopt('ArrayToSt
               padding1,padding0,class(item),padding0,regexprep(mat2str(size(item)),'\s+',',') );
     else
     	txt=sprintf('%s"%s": {\n%s"_ArrayType_": "%s",\n%s"_ArraySize_": %s,\n',...
-              padding1,name,padding0,class(item),padding0,regexprep(mat2str(size(item)),'\s+',',') );
+              padding1,checkname(name,varargin{:}),padding0,class(item),padding0,regexprep(mat2str(size(item)),'\s+',',') );
     end
 else
     if(isempty(name))
@@ -257,9 +260,9 @@ else
     else
         if(numel(item)==1 && jsonopt('NoRowBracket',1,varargin{:})==1)
             numtxt=regexprep(regexprep(matdata2json(item,level+1,varargin{:}),'^\[',''),']','');
-           	txt=sprintf('%s"%s": %s',padding1,name,numtxt);
+           	txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),numtxt);
         else
-    	    txt=sprintf('%s"%s": %s',padding1,name,matdata2json(item,level+1,varargin{:}));
+    	    txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),matdata2json(item,level+1,varargin{:}));
         end
     end
     return;
@@ -340,3 +343,30 @@ opt=varargin{1};
 if(isstruct(opt) && isfield(opt,key))
     val=getfield(opt,key);
 end
+%%-------------------------------------------------------------------------
+function newname=checkname(name,varargin)
+isunpack=jsonopt('UnpackHex',1,varargin{:});
+newname=name;
+if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once')))
+    return
+end
+if(isunpack)
+    isoct=jsonopt('IsOctave',0,varargin{:});
+    if(~isoct)
+        newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}');
+    else
+        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)) return; end
+        str0=name;
+        pos0=[0 pend(:)' length(name)];
+        newname='';
+        for i=1:length(pos)
+            newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))];
+        end
+        if(pos(end)~=length(name))
+            newname=[newname str0(pos0(end-1)+1:pos0(end))];
+        end
+    end
+end
+