Explorar el Código

* Added fast dumpjson method, formatting option to skip whitespace

git-svn-id: trunk@28818 -
michael hace 10 años
padre
commit
2d553068f4
Se han modificado 2 ficheros con 127 adiciones y 61 borrados
  1. 112 48
      packages/fcl-json/src/fpjson.pp
  2. 15 13
      packages/fcl-json/tests/testjsondata.pp

+ 112 - 48
packages/fcl-json/src/fpjson.pp

@@ -36,14 +36,17 @@ type
   TFormatOption = (foSingleLineArray,   // Array without CR/LF : all on one line
   TFormatOption = (foSingleLineArray,   // Array without CR/LF : all on one line
                    foSingleLineObject,  // Object without CR/LF : all on one line
                    foSingleLineObject,  // Object without CR/LF : all on one line
                    foDoNotQuoteMembers, // Do not quote object member names.
                    foDoNotQuoteMembers, // Do not quote object member names.
-                   foUseTabchar);       // Use tab characters instead of spaces.
+                   foUseTabchar,        // Use tab characters instead of spaces.
+                   foSkipWhiteSpace);   // Do not use whitespace at all
   TFormatOptions = set of TFormatOption;
   TFormatOptions = set of TFormatOption;
 
 
 Const
 Const
   DefaultIndentSize = 2;
   DefaultIndentSize = 2;
   DefaultFormat     = [];
   DefaultFormat     = [];
   AsJSONFormat      = [foSingleLineArray,foSingleLineObject]; // These options make FormatJSON behave as AsJSON
   AsJSONFormat      = [foSingleLineArray,foSingleLineObject]; // These options make FormatJSON behave as AsJSON
-  
+  AsCompressedJSON  = [foSingleLineArray,foSingleLineObject,foskipWhiteSpace]; // These options make FormatJSON behave as AsJSON with TJSONData.CompressedJSON=True
+  AsCompactJSON     = [foSingleLineArray,foSingleLineObject,foskipWhiteSpace,foDoNotQuoteMembers]; // These options make FormatJSON behave as AsJSON with TJSONData.CompressedJSON=True and TJSONObject.UnquotedMemberNames=True
+
 Type
 Type
   TJSONData = Class;
   TJSONData = Class;
 
 
@@ -68,6 +71,8 @@ Type
   
   
   TJSONData = class(TObject)
   TJSONData = class(TObject)
   private
   private
+    Const
+      ElementSeps  : Array[Boolean] of TJSONStringType = (', ',',');
     Class Var FCompressedJSON : Boolean;
     Class Var FCompressedJSON : Boolean;
     Class Var FElementSep : TJSONStringType;
     Class Var FElementSep : TJSONStringType;
     class procedure DetermineElementSeparators;
     class procedure DetermineElementSeparators;
@@ -103,6 +108,7 @@ Type
   public
   public
     Constructor Create; virtual;
     Constructor Create; virtual;
     Procedure Clear;  virtual; Abstract;
     Procedure Clear;  virtual; Abstract;
+    Procedure DumpJSON(S : TStream);
     // Get enumerator
     // Get enumerator
     function GetEnumerator: TBaseJSONEnumerator; virtual;
     function GetEnumerator: TBaseJSONEnumerator; virtual;
     Function FindPath(Const APath : TJSONStringType) : TJSONdata;
     Function FindPath(Const APath : TJSONStringType) : TJSONdata;
@@ -442,7 +448,13 @@ Type
 
 
   TJSONObject = class(TJSONData)
   TJSONObject = class(TJSONData)
   private
   private
-    Class var FUnquotedElementNames: Boolean;
+    Const
+      ElementStart   : Array[Boolean] of TJSONStringType = ('"','');
+      SpacedQuoted   : Array[Boolean] of TJSONStringType = ('" : ',' : ');
+      UnSpacedQuoted : Array[Boolean] of TJSONStringType = ('":',':');
+      ObjStartSeps   : Array[Boolean] of TJSONStringType = ('{ ','{');
+      ObjEndSeps     : Array[Boolean] of TJSONStringType = (' }','}');
+    Class var FUnquotedMemberNames: Boolean;
     Class var FObjStartSep,FObjEndSep,FElementEnd,FElementStart : TJSONStringType;
     Class var FObjStartSep,FObjEndSep,FElementEnd,FElementStart : TJSONStringType;
     Class procedure DetermineElementQuotes;
     Class procedure DetermineElementQuotes;
   Private
   Private
@@ -469,8 +481,8 @@ Type
     procedure SetObjects(const AName : String; const AValue: TJSONObject);
     procedure SetObjects(const AName : String; const AValue: TJSONObject);
     procedure SetQWords(AName : String; AValue: QWord);
     procedure SetQWords(AName : String; AValue: QWord);
     procedure SetStrings(const AName : String; const AValue: TJSONStringType);
     procedure SetStrings(const AName : String; const AValue: TJSONStringType);
-    class function GetUnquotedElementNames: Boolean; static;
-    class procedure SetUnquotedElementNames(AValue: Boolean); static;
+    class function GetUnquotedMemberNames: Boolean; static;
+    class procedure SetUnquotedMemberNames(AValue: Boolean); static;
   protected
   protected
     Function DoFindPath(Const APath : TJSONStringType; Out NotFound : TJSONStringType) : TJSONdata; override;
     Function DoFindPath(Const APath : TJSONStringType; Out NotFound : TJSONStringType) : TJSONdata; override;
     Procedure Converterror(From : Boolean);
     Procedure Converterror(From : Boolean);
@@ -498,7 +510,7 @@ Type
     Constructor Create(const Elements : Array of Const); overload;
     Constructor Create(const Elements : Array of Const); overload;
     destructor Destroy; override;
     destructor Destroy; override;
     class function JSONType: TJSONType; override;
     class function JSONType: TJSONType; override;
-    Class Property UnquotedElementNames : Boolean Read GetUnquotedElementNames Write SetUnquotedElementNames;
+    Class Property UnquotedMemberNames : Boolean Read GetUnquotedMemberNames Write SetUnquotedMemberNames;
     Function Clone : TJSONData; override;
     Function Clone : TJSONData; override;
     function GetEnumerator: TBaseJSONEnumerator; override;
     function GetEnumerator: TBaseJSONEnumerator; override;
     // Examine
     // Examine
@@ -1011,6 +1023,52 @@ begin
   Clear;
   Clear;
 end;
 end;
 
 
+procedure TJSONData.DumpJSON(S: TStream);
+
+  Procedure W(T : String);
+
+  begin
+    if (T<>'') then
+      S.WriteBuffer(T[1],Length(T)*SizeOf(Char));
+  end;
+
+Var
+  I,C : Integer;
+  O : TJSONObject;
+
+begin
+  Case JSONType of
+    jtObject :
+      begin
+      O:=TJSONObject(Self);
+      W('{');
+      For I:=0 to O.Count-1 do
+        begin
+        if (I>0) then
+          W(',');
+        W('"');
+        W(StringToJSONString(O.Names[i]));
+        W('":');
+        O.Items[I].DumpJSON(S);
+        end;
+      W('}');
+      end;
+    jtArray :
+      begin
+      W('[');
+      For I:=0 to Count-1 do
+        begin
+        if (I>0) then
+          W(',');
+        Items[I].DumpJSON(S);
+        end;
+      W(']');
+      end
+  else
+    W(AsJSON)
+  end;
+end;
+
 class function TJSONData.GetCompressedJSON: Boolean; static;
 class function TJSONData.GetCompressedJSON: Boolean; static;
 begin
 begin
   Result:=FCompressedJSON;
   Result:=FCompressedJSON;
@@ -1018,8 +1076,6 @@ end;
 
 
 class procedure TJSONData.DetermineElementSeparators;
 class procedure TJSONData.DetermineElementSeparators;
 
 
-Const
-  ElementSeps  : Array[Boolean] of TJSONStringType = (', ',',');
 
 
 begin
 begin
   FElementSep:=ElementSeps[FCompressedJSON];
   FElementSep:=ElementSeps[FCompressedJSON];
@@ -1963,25 +2019,31 @@ function TJSONArray.DoFormatJSON(Options: TFormatOptions; CurrentIndent,
 
 
 Var
 Var
   I : Integer;
   I : Integer;
+  MultiLine : Boolean;
+  SkipWhiteSpace : Boolean;
+  Ind : String;
   
   
 begin
 begin
   Result:='[';
   Result:='[';
-  if not (foSingleLineArray in Options) then
+  MultiLine:=Not (foSingleLineArray in Options);
+  SkipWhiteSpace:=foSkipWhiteSpace in Options;
+  Ind:=IndentString(Options, CurrentIndent+Indent);
+  if MultiLine then
     Result:=Result+sLineBreak;
     Result:=Result+sLineBreak;
   For I:=0 to Count-1 do
   For I:=0 to Count-1 do
     begin
     begin
-    if not (foSingleLineArray in Options) then
-      Result:=Result+IndentString(Options, CurrentIndent+Indent);
+    if MultiLine then
+      Result:=Result+Ind;
     Result:=Result+Items[i].DoFormatJSON(Options,CurrentIndent+Indent,Indent);
     Result:=Result+Items[i].DoFormatJSON(Options,CurrentIndent+Indent,Indent);
     If (I<Count-1) then
     If (I<Count-1) then
-      if (foSingleLineArray in Options) then
-        Result:=Result+', '
+      if MultiLine then
+        Result:=Result+','
       else
       else
-        Result:=Result+',';
-    if not (foSingleLineArray in Options) then
+        Result:=Result+ElementSeps[SkipWhiteSpace];
+    if MultiLine then
       Result:=Result+sLineBreak
       Result:=Result+sLineBreak
     end;
     end;
- if not (foSingleLineArray in Options) then
+  if MultiLine then
     Result:=Result+IndentString(Options, CurrentIndent);
     Result:=Result+IndentString(Options, CurrentIndent);
   Result:=Result+']';
   Result:=Result+']';
 end;
 end;
@@ -2337,9 +2399,9 @@ begin
   Result:=Getelements(Aname).JSONType;
   Result:=Getelements(Aname).JSONType;
 end;
 end;
 
 
-class function TJSONObject.GetUnquotedElementNames: Boolean; static;
+class function TJSONObject.GetUnquotedMemberNames: Boolean; static;
 begin
 begin
-  Result:=FUnquotedElementNames;
+  Result:=FUnquotedMemberNames;
 end;
 end;
 
 
 procedure TJSONObject.SetArrays(const AName : String; const AValue: TJSONArray);
 procedure TJSONObject.SetArrays(const AName : String; const AValue: TJSONArray);
@@ -2404,28 +2466,21 @@ end;
 
 
 class procedure TJSONObject.DetermineElementQuotes;
 class procedure TJSONObject.DetermineElementQuotes;
 
 
-Const
-  ElementStart   : Array[Boolean] of TJSONStringType = ('"','');
-  SpacedQuoted   : Array[Boolean] of TJSONStringType = ('" : ',' : ');
-  UnSpacedQuoted : Array[Boolean] of TJSONStringType = ('":',':');
-  ObjStartSeps   : Array[Boolean] of TJSONStringType = ('{ ','{');
-  ObjEndSeps     : Array[Boolean] of TJSONStringType = (' }','}');
-
 begin
 begin
   FObjStartSep:=ObjStartSeps[TJSONData.FCompressedJSON];
   FObjStartSep:=ObjStartSeps[TJSONData.FCompressedJSON];
   FObjEndSep:=ObjEndSeps[TJSONData.FCompressedJSON];
   FObjEndSep:=ObjEndSeps[TJSONData.FCompressedJSON];
   if TJSONData.FCompressedJSON then
   if TJSONData.FCompressedJSON then
-    FElementEnd:=UnSpacedQuoted[FUnquotedElementNames]
+    FElementEnd:=UnSpacedQuoted[FUnquotedMemberNames]
   else
   else
-    FElementEnd:=SpacedQuoted[FUnquotedElementNames];
-  FElementStart:=ElementStart[FUnquotedElementNames]
+    FElementEnd:=SpacedQuoted[FUnquotedMemberNames];
+  FElementStart:=ElementStart[FUnquotedMemberNames]
 end;
 end;
 
 
-class procedure TJSONObject.SetUnquotedElementNames(AValue: Boolean); static;
+class procedure TJSONObject.SetUnquotedMemberNames(AValue: Boolean); static;
 
 
 begin
 begin
-  if FUnquotedElementNames=AValue then exit;
-  FUnquotedElementNames:=AValue;
+  if FUnquotedMemberNames=AValue then exit;
+  FUnquotedMemberNames:=AValue;
   DetermineElementQuotes;
   DetermineElementQuotes;
 end;
 end;
 
 
@@ -2664,33 +2719,42 @@ function TJSONObject.DoFormatJSON(Options: TFormatOptions; CurrentIndent,
 Var
 Var
   i : Integer;
   i : Integer;
   S : TJSONStringType;
   S : TJSONStringType;
-
-
+  MultiLine,UseQuotes, SkipWhiteSpace : Boolean;
+  NSep,Sep,Ind : String;
 begin
 begin
   Result:='';
   Result:='';
-  CurrentIndent:=CurrentIndent+Indent;  
+  UseQuotes:=Not (foDoNotQuoteMembers in options);
+  MultiLine:=Not (foSingleLineObject in Options);
+  SkipWhiteSpace:=foSkipWhiteSpace in Options;
+  CurrentIndent:=CurrentIndent+Indent;
+  Ind:=IndentString(Options, CurrentIndent);
+  If SkipWhiteSpace then
+    NSep:=':'
+  else
+    NSep:=' : ';
+  If MultiLine then
+    Sep:=','+SLineBreak+Ind
+  else if SkipWhiteSpace then
+    Sep:=','
+  else
+    Sep:=', ';
   For I:=0 to Count-1 do
   For I:=0 to Count-1 do
     begin
     begin
-    If (Result<>'') then
-      begin
-      If (foSingleLineObject in Options) then
-        Result:=Result+', '
-      else
-        Result:=Result+','+SLineBreak;
-      end;
-    If not (foSingleLineObject in Options) then    
-      Result:=Result+IndentString(Options,CurrentIndent);
+    If (I>0) then
+      Result:=Result+Sep
+    else If MultiLine then
+      Result:=Result+Ind;
     S:=StringToJSONString(Names[i]);
     S:=StringToJSONString(Names[i]);
-    If not (foDoNotQuoteMembers in options) then
+    If UseQuotes then
       S:='"'+S+'"';
       S:='"'+S+'"';
-    Result:=Result+S+' : '+Items[I].DoFormatJSON(Options,CurrentIndent,Indent);
+    Result:=Result+S+NSep+Items[I].DoFormatJSON(Options,CurrentIndent,Indent);
     end;
     end;
   If (Result<>'') then
   If (Result<>'') then
     begin
     begin
-    if (foSingleLineObject in Options) then
-      Result:='{ '+Result+' }'
-    else  
+    if MultiLine then
       Result:='{'+sLineBreak+Result+sLineBreak+indentString(options,CurrentIndent-Indent)+'}'
       Result:='{'+sLineBreak+Result+sLineBreak+indentString(options,CurrentIndent-Indent)+'}'
+    else
+      Result:=ObjStartSeps[SkipWhiteSpace]+Result+ObjEndSeps[SkipWhiteSpace]
     end
     end
   else
   else
     Result:='{}';
     Result:='{}';

+ 15 - 13
packages/fcl-json/tests/testjsondata.pp

@@ -1054,7 +1054,7 @@ begin
   inherited SetUp;
   inherited SetUp;
   SetDefaultInstanceTypes;
   SetDefaultInstanceTypes;
   TJSONData.CompressedJSON:=False;
   TJSONData.CompressedJSON:=False;
-  TJSONObject.UnquotedElementNames:=False;
+  TJSONObject.UnquotedMemberNames:=False;
 end;
 end;
 
 
 Procedure TTestJSON.TestItemCount(J: TJSONData; Expected: Integer);
 Procedure TTestJSON.TestItemCount(J: TJSONData; Expected: Integer);
@@ -2994,10 +2994,10 @@ begin
     TestJSONType(J[2],jtNumber);
     TestJSONType(J[2],jtNumber);
     TestJSON(J,'[0, 1, 2]');
     TestJSON(J,'[0, 1, 2]');
     AssertEquals('FormatJSON, single line',J.AsJSON,J.FormatJSON([foSingleLineArray],1));
     AssertEquals('FormatJSON, single line',J.AsJSON,J.FormatJSON([foSingleLineArray],1));
-    AssertEquals('FormatJSON, single line','['+sLinebreak+'  0,'+sLinebreak+'  1,'+sLinebreak+'  2'+sLinebreak+']',J.FormatJSON());
-    AssertEquals('FormatJSON, single line','['+sLinebreak+#9'0,'+sLinebreak+#9'1,'+sLinebreak+#9'2'+sLinebreak+']',J.FormatJSON([foUseTabChar],1));
+    AssertEquals('FormatJSON, default','['+sLinebreak+'  0,'+sLinebreak+'  1,'+sLinebreak+'  2'+sLinebreak+']',J.FormatJSON());
+    AssertEquals('FormatJSON, use tab','['+sLinebreak+#9'0,'+sLinebreak+#9'1,'+sLinebreak+#9'2'+sLinebreak+']',J.FormatJSON([foUseTabChar],1));
     J.Add(TJSONObject.Create(['x',1,'y',2]));
     J.Add(TJSONObject.Create(['x',1,'y',2]));
-    AssertEquals('FormatJSON, single line','['+sLinebreak+#9'0,'+sLinebreak+#9'1,'+sLinebreak+#9'2,'+sLinebreak+#9'{'+sLineBreak+#9#9'"x" : 1,'+sLineBreak+#9#9'"y" : 2'+sLinebreak+#9'}'+sLineBreak+']',J.FormatJSON([foUseTabChar],1));
+    AssertEquals('FormatJSON, use tab indentsize 1','['+sLinebreak+#9'0,'+sLinebreak+#9'1,'+sLinebreak+#9'2,'+sLinebreak+#9'{'+sLineBreak+#9#9'"x" : 1,'+sLineBreak+#9#9'"y" : 2'+sLinebreak+#9'}'+sLineBreak+']',J.FormatJSON([foUseTabChar],1));
   finally
   finally
     J.Free
     J.Free
   end;
   end;
@@ -3440,6 +3440,8 @@ begin
   try
   try
     TestJSON(O,'{ "x" : 1, "y" : 2 }');
     TestJSON(O,'{ "x" : 1, "y" : 2 }');
     AssertEquals('Format equals JSON',O.AsJSON,O.FormatJSON([foSingleLineObject]));
     AssertEquals('Format equals JSON',O.AsJSON,O.FormatJSON([foSingleLineObject]));
+    AssertEquals('Format using SkipWhiteSpace','{"x":1,"y":2}',O.FormatJSON([foSingleLineObject,foSkipWhiteSpace]));
+    AssertEquals('Format using SkipWhiteSpace,unquotednames','{x:1,y:2}',O.FormatJSON([foSingleLineObject,foSkipWhiteSpace,foDoNotQuoteMembers]));
     AssertEquals('Format 1','{'+sLineBreak+'  "x" : 1,'+sLineBreak+'  "y" : 2'+sLineBreak+'}',O.FormatJSON([]));
     AssertEquals('Format 1','{'+sLineBreak+'  "x" : 1,'+sLineBreak+'  "y" : 2'+sLineBreak+'}',O.FormatJSON([]));
     AssertEquals('Format 1','{'+sLineBreak+'  x : 1,'+sLineBreak+'  y : 2'+sLineBreak+'}',O.FormatJSON([foDoNotQuoteMembers]));
     AssertEquals('Format 1','{'+sLineBreak+'  x : 1,'+sLineBreak+'  y : 2'+sLineBreak+'}',O.FormatJSON([foDoNotQuoteMembers]));
     AssertEquals('Format 1','{'+sLineBreak+#9'x : 1,'+sLineBreak+#9'y : 2'+sLineBreak+'}',O.FormatJSON([foUseTabChar,foDoNotQuoteMembers],1));
     AssertEquals('Format 1','{'+sLineBreak+#9'x : 1,'+sLineBreak+#9'y : 2'+sLineBreak+'}',O.FormatJSON([foUseTabChar,foDoNotQuoteMembers],1));
@@ -3517,7 +3519,7 @@ Var
   J : TJSONObject;
   J : TJSONObject;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnquotedMemberNames:=True;
   J:=TJSONObject.Create([A,S]);
   J:=TJSONObject.Create([A,S]);
   try
   try
     TestJSONType(J,jtObject);
     TestJSONType(J,jtObject);
@@ -3562,7 +3564,7 @@ Var
   J : TJSONObject;
   J : TJSONObject;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,Pchar(S)]);
   J:=TJSONObject.Create([A,Pchar(S)]);
   try
   try
     TestJSONType(J,jtObject);
     TestJSONType(J,jtObject);
@@ -3639,7 +3641,7 @@ Var
 
 
 begin
 begin
   TJSONData.CompressedJSON:=True;
   TJSONData.CompressedJSON:=True;
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,S,B,T]);
   J:=TJSONObject.Create([A,S,B,T]);
   try
   try
     TestJSONType(J,jtObject);
     TestJSONType(J,jtObject);
@@ -3683,7 +3685,7 @@ Var
   J : TJSONObject;
   J : TJSONObject;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,S]);
   J:=TJSONObject.Create([A,S]);
   try
   try
     TestJSONType(J,jtObject);
     TestJSONType(J,jtObject);
@@ -3728,7 +3730,7 @@ Var
   r : String;
   r : String;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,S]);
   J:=TJSONObject.Create([A,S]);
   try
   try
     TestJSONType(J,jtObject);
     TestJSONType(J,jtObject);
@@ -3771,7 +3773,7 @@ Var
   J : TJSONObject;
   J : TJSONObject;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,S]);
   J:=TJSONObject.Create([A,S]);
   try
   try
     TestJSONType(J,jtObject);
     TestJSONType(J,jtObject);
@@ -3813,7 +3815,7 @@ Var
   J : TJSONObject;
   J : TJSONObject;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,S]);
   J:=TJSONObject.Create([A,S]);
   try
   try
     TestJSONType(J,jtObject);
     TestJSONType(J,jtObject);
@@ -3852,7 +3854,7 @@ Var
   J : TJSONObject;
   J : TJSONObject;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,TJSONObject.Create]);
   J:=TJSONObject.Create([A,TJSONObject.Create]);
   try
   try
     TestItemCount(J,1);
     TestItemCount(J,1);
@@ -3893,7 +3895,7 @@ Var
   J : TJSONObject;
   J : TJSONObject;
 
 
 begin
 begin
-  TJSONObject.UnquotedElementNames:=True;
+  TJSONObject.UnQuotedMemberNames:=True;
   J:=TJSONObject.Create([A,TJSONString.Create(S)]);
   J:=TJSONObject.Create([A,TJSONString.Create(S)]);
   try
   try
     TestItemCount(J,1);
     TestItemCount(J,1);