close

Delphi XE10 已經有內建的Json處理Unit 請見 http://kamilia1470.pixnet.net/blog/post/217835349###

Delphi 7 網路上也有別人用好的Json Unit 感覺跟XE差不多

但是我覺得很難用 要搞很久才懂他的意思 而且不能直接知道有多少物件

要是Range下錯就直接跳錯

 

於是自己弄了一個Unit來處理Json

Json 格式我就簡單介紹一下 詳細的還是得自己Google

總而言之

最基本的就是 Key:Value

我稱之為 BaseJson

 

{ } 包起來的就是物件 裡面可以有很多BaseItem 也可以有很多 "Value 為 陣列的Item"

[ ] 包起來的就是陣列 裡面可以有很多物件

 

總而言之 就是 物件裡面可以有陣列裡面又有物件 或是 陣列裡面有物件又有陣列又有物件......

最外層可以是物件 也可以是陣列 麻煩就在這

 

以下是我自己寫的Json Unit

(*
(* TBaseJson使用
var bj:TBaseJson;
begin
  {
  with bj do
  begin
    Key:='123';
    Value:='abc';
    ShowMessage(Source);
  end
  }
  {
  with bj do
  begin
    Source:='"123":"abc"';
    ShowMessage(Key+#13+Value);
  end
  }

(* TOneJsonObject 使用
var
  jo:TOneJsonObject;
  i:integer;
begin
  //BaseJsonItem
  jo.Source:='{"abc":"123","cde":"456","ttt":[{"ace":"789"}],"ijk":"567"}';
  for I := 0 to length(jo.JsonItems)-1 do
  begin
    if jo.JsonItems[i].IsBaseJson then
      ShowMessage('T')
    else
      ShowMessage('F');

    ShowMessage(jo.JsonItems[i].Source);
  end;

(* TjsonArray 範例

var
  jo:TOneJsonObject;
  i:integer;
begin
  //BaseJsonItem
  jo.Source:='{"abc":"123","cde":"456","ttt":[{"ace":"789"},{"ppp":"555","kkk":"666"}],"ijk":"567"}';
                                                                             // ↑↑↑
  ShowMessage(jo.JsonItems[2].ValueAsArray.JsonObjects[1].JsonItems[1].Value);

(* *)
unit XBJsonUnit;

interface

uses System.Classes,System.SysUtils,SysConst;

Type
  TBaseJson = class    //JSon最底層 沒有陣列 單一個key = Value型態
  private
    FKey:String;
    FValue:string;
    Function GetSource:String;
    procedure SetSource(Value: String);
    procedure SetKey(Value: String);
    procedure SetValue(Value: String);
  published
    property Source: String read GetSource write SetSource;
    property Key: String read FKey write SetKey;
    property Value: String read FValue write SetValue;
  end;

  TJsonItem = class;

  TJsonObject = record           //用 { } 包起來的 為 TOneJsonObject 裡面可以有很多 TJsonItem
    JsonItems:array of TJsonItem;
  private
    FSource:string;
    procedure SetSource(Value: String);
  public
    property Source: String read FSource write SetSource;
  end;

  TJsonArray = record               //用 [ ] 包起來的 為 TJsonArray 裡面可以有很多 TOneJsonObject
    JsonObjects:array of TJsonObject;
  private
    FSource:string;
    procedure SetSource(Value: String);
  public
    property Source: String read FSource write SetSource;
  end;

  TJsonItem = class(TBaseJson)     //指單一個item (與下一個item 用,分隔的) 只會有兩種型態 1=BaseJson (key = Value) 2=含有陣列的Json (key = TJsonArray)
  public
    function IsBaseJson:Boolean;
  private
    Function GetValueAsArray:TJsonArray;
    Procedure  SetValueAsArray(Value: TJsonArray);
  published
    property ValueAsArray: TJsonArray read GetValueAsArray write SetValueAsArray;
  end;

implementation

{ TBaseJson }

procedure TBaseJson.SetSource(Value: String);
begin
  while Pos('"',Value)>0 do
    Delete(Value,Pos('"',Value),1);
  while Pos(' ',Value)>0 do
    Delete(Value,Pos(' ',Value),1);
  FKey:=Copy(Value,1,Pos(':',Value)-1);
  FValue:=Copy(Value,Pos(':',Value)+1,Length(Value)-Pos(':',Value));
end;

function TBaseJson.GetSource: String;
begin
  Result:='"'+FKey+'":"'+FValue+'"';
end;

procedure TBaseJson.SetKey(Value: String);
begin
  if FKey<>Value then
    FKey:=Value;
end;

procedure TBaseJson.SetValue(Value: String);
begin
  if FValue<>Value then
    FValue:=Value;
end;

{ TJsonArray }

procedure TJsonArray.SetSource(Value: String);
var
  JSonArrayStr:String; //拿來分解的字串
  i,OCount,PosD:integer;
  JsonObjectData:TStrings; //拿來裝所有JsonObject資料
begin
  if FSource=Value then
    Exit;
  FSource:=Value;

  //預防空直
  if Source='' then
  begin
    raise EConvertError.CreateFmt(System.SysConst.SInvalidSourceArray, ['Empty']);
    Exit;
  end;

  //一定要是 [開頭 ]結尾
  if (Source[1] <> '[') or (Source[Length(Source)] <> ']') then
  begin
    raise EConvertError.CreateFmt(System.SysConst.SInvalidSourceArray, ['"[" and "]"']);
    Exit;
  end;

  JSonArrayStr:=Source;
  //先去掉頭尾 { }
  Delete(JSonArrayStr,1,1);
  Delete(JSonArrayStr,Length(JSonArrayStr),1);

  JsonObjectData:=TStringlist.Create;

  repeat
    PosD:=Pos('},{',JSonArrayStr);
    if PosD=0 then //找不到逗號 等於結束
    begin
      JsonObjectData.Append(JSonArrayStr);
    end
    else
    begin
      //陣列裡面 一定是 {object1},{object2],...,{objectlast} 所以一定要是 {開頭 {結尾
      if (JSonArrayStr[1] <> '{') or (JSonArrayStr[Length(JSonArrayStr)] <> '}') then
      begin
        raise EConvertError.CreateFmt(System.SysConst.SInvalidSourceArray,
          ['Array inside must be begin "{" end "}"']);
        Exit;
      end;

      OCount:=0;
      for I := 1 to Length(JSonArrayStr) do //從 [ 後面的字元開始數 遇到 [ + 1 遇到 ] -1 直到 ACount 等於0
      begin
        case JSonArrayStr[i] of
          '{':Inc(OCount);
          '}':Dec(OCount);
        end;
        if OCount=0 then
          Break;
      end;
      //  } 後面 一定是逗號 (最後一個的話 不會進到這裡)
      JsonObjectData.Append(Copy(JSonArrayStr,1,i));
      Delete(JSonArrayStr,1,i+1);
    end;
  until PosD=0;

  SetLength(JsonObjects,JsonObjectData.Count);

  for I := 0 to JsonObjectData.Count-1 do
    JsonObjects[i].Source:=JsonObjectData[i];

  JsonObjectData.free;
end;

{ TOneJsonObject }

procedure TJsonObject.SetSource(Value: String);
var
  JsonObjectStr:string; //拿來分解的字串
  bufArrayStr:string; //若遇到陣列 要佔存字串
  i,PosD,PosA,ACount:Integer; //下一個位置  PosD = , PosA = [    ACount 用來數陣列 的 結束位置
  JsonItemData:TStrings; //拿來裝所有Item資料
  procedure GetNextItem;
  begin
    JsonItemData.Append(Copy(JsonObjectStr,1,PosD-1));
    Delete(JsonObjectStr,1,PosD);
  end;
begin
  if FSource=Value then
    Exit;
  FSource:=Value;
  //用 { } 包起來的 為 TOneJsonObject 裡面可以有很多 TJsonItem

  //預防空直
  if Source='' then
  begin
    raise EConvertError.CreateFmt(System.SysConst.SInvalidSourceArray, ['Empty']);
    Exit;
  end;

  //一定要是 {開頭 {結尾
  if (Source[1] <> '{') or (Source[Length(Source)] <> '}') then
  begin
    raise EConvertError.CreateFmt(System.SysConst.SInvalidSourceArray, ['"{" and "}"']);
    Exit;
  end;

  JsonObjectStr:=Source;
  //先去掉頭尾 { }
  Delete(JsonObjectStr,1,1);
  Delete(JsonObjectStr,Length(JsonObjectStr),1);

  JsonItemData:=TStringList.Create;
  repeat
    PosD:=Pos(',',JsonObjectStr);
    if PosD=0 then //找不到逗號 等於結束
    begin
      JsonItemData.Append(JsonObjectStr);
    end
    else
    begin
      PosA:= Pos('[',JsonObjectStr); //下一個陣列位置
      if PosA=0 then  //若沒有陣列 也就是最原始的物件 只有{item,item} 這樣
      begin
        GetNextItem;
      end
      else //若有陣列 就會像這樣 {itme,item,key:[...],item}
      begin
        if PosA>PosD then //若 [ 比 , 遠 代表下一個item 還是最基本的item
        begin
          GetNextItem;
        end
        else //若 [ 比 , 近 代表下一個item 就是包含array的item
        begin
          ACount:=1;
          for I := PosA+1 to Length(JsonObjectStr) do //從 [ 後面的字元開始數 遇到 [ + 1 遇到 ] -1 直到 ACount 等於0
          begin
            case JsonObjectStr[i] of
              '[':Inc(ACount);
              ']':Dec(ACount);
            end;
            if ACount=0 then
              Break;
          end;
          //  ] 後面 一定是逗號 (最後一個的話 不會進到這裡)
          JsonItemData.Append(Copy(JsonObjectStr,1,i));
          Delete(JsonObjectStr,1,i+1);
        end;
      end;
    end;
  until PosD=0;

  SetLength(JsonItems,JsonItemData.Count);

  for I := 0 to JsonItemData.Count-1 do
  begin
    JsonItems[i]:=TJsonItem.Create;
    JsonItems[i].Source:=JsonItemData[i];
  end;

  JsonItemData.Free;
end;

{ TJsonItem }

function TJsonItem.IsBaseJson: Boolean;
begin
  Result:= Pos('[',FValue)=0; //沒有陣列 就是最底層
end;

function TJsonItem.GetValueAsArray: TJsonArray;
begin
  if IsBaseJson then
  begin
    raise EConvertError.CreateFmt(System.SysConst.SInvalidSourceArray, [Source]);
    Exit;
  end;
  Result.Source:=FValue;
end;

procedure TJsonItem.SetValueAsArray(Value: TJsonArray);
begin
  FValue:=Value.Source;
end;

end.


範例 >> 使用樹狀圖 呈現資料

 

procedure TForm1.Button1Click(Sender: TObject);
  Procedure BuildTree(JsonStr:string;PNode:TTreeNode);
  var
    jo:TJsonObject;
    ja:TJsonArray;
    i:Integer;
    NNode:TTreeNode;
  begin
    if JsonStr='' then
      Exit;
    case JsonStr[1] of
      '{':   //物件
      begin
        jo.Source:=JsonStr;
        for I := 0 to length(jo.JsonItems)-1 do
          with jo.JsonItems[i] do
            if IsBaseJson then
              TreeView1.Items.AddChild(PNode,'Item '+ IntToStr(i) + ' ' + Source)
            else
            begin
              NNode:=TreeView1.Items.AddChild(PNode,'Item '+ IntToStr(i) + ' Key = ' + Key);
              BuildTree(ValueAsArray.Source,NNode); //遇到陣列 就往下一階開始建立
            end;
      end;
      '[':   //陣列
      begin
        ja.Source:=JsonStr;
        for I := 0 to Length(ja.JsonObjects)-1 do
          with ja.JsonObjects[i] do
            begin
              NNode:=TreeView1.Items.AddChild(PNode,'object '+ IntToStr(i));
              BuildTree(Source,NNode); //每一個物件都要往下一階建立
            end;
      end;
      else
      begin
        ShowMessage('錯誤參數');
        Exit;
      end;
    end;
  end;
begin
  BuildTree(Edit1.Text,nil);
end;
 

附上檔案

https://drive.google.com/open?id=0B_Boa0V6iHIuWGdtOXczQ0ZSdmM


以下代碼 為XE版本 可以直接使用Json "陣列" 直接轉為TClientDataSet (與上面無關)

是我自己在用的Json處理的Unit

 

unit XBJsonUnit;

interface

uses System.Classes,IdHTTP,JSon,FMX.Dialogs,
     REST.Response.Adapter, REST.Client,
     Data.Bind.Components, Data.Bind.ObjectScope,
     Datasnap.DBClient
     ,IPPeerClient //不USE 可以過 但會跳一個很奇怪的錯誤 (要拉REST元件 才會自動USE這個)
     ;

type
  TRestJsonCDS=class(TClientDataSet)
    RESTClient: TRESTClient;
    RESTRequest: TRESTRequest;
    RESTResponse: TRESTResponse;
    RESTResponseDataSetAdapter: TRESTResponseDataSetAdapter;
  public
    constructor Create(AOwner: TComponent;BaseURL:String;ParaTs:TStrings=nil); overload;
    constructor Create(AOwner: TComponent;BaseURL:String;ParaStr:array of String); overload;
    destructor destroy; override;
  end;

function GetPostReturn(URL:string;ParaTs:TStrings;var ReturnStr:String):Boolean; overload;
function GetPostReturn(URL:string;ParaTs:TStrings):string; overload;

function GetXBJson(BaseUrl,MainFunctionName,SubFunctionName:string;ParaTs:TStrings):string; overload;
function GetXBJson(BaseUrl,MainFunctionName,SubFunctionName:string;ParaStr:array of String):string; overload;

function GetXBJSonCDS(BaseURL,MainFunctionName,SubFunctionName:string;ParaTs:TStrings=nil):TRestJsonCDS; overload; //此函數將自動產生一個CDS 需自行釋放
function GetXBJSonCDS(BaseURL,MainFunctionName,SubFunctionName:string;ParaStr:array of String):TRestJsonCDS; overload; //此函數將自動產生一個CDS 需自行釋放

function GetXBJsonOneField(BaseURL,MainFunctionName,SubFunctionName:string;ParaStr:array of String;FieldName:String):string;

Const EmptyRecord=''; //當CDS為完全空值時回傳此字串 未來有可能會回傳空字串時可以修改這裡 以區分空值跟空DB

implementation

uses REST.Types;

function GetFullURL(BaseUrl,MainFunctionName,SubFunctionName:string):String;
begin
  result:= BaseUrl+'/'+MainFunctionName+'/'+SubFunctionName;
end;

function GetPostReturn(URL:string;ParaTs:TStrings;var ReturnStr:String):Boolean;
var idhttp:TIdHttp;
begin
  Result:=True;
  ReturnStr:='';
  idhttp:=TIdHttp.Create(nil);
  try
    try
      showmessage('URL>>'+URL+#13+ParaTs.Text);
      ReturnStr:=IdHTTP.Post(URL,ParaTs);
    except
      Result:=False;
    end;
  finally
    idhttp.Free;
  end;
end;

function GetPostReturn(URL:string;ParaTs:TStrings):string; overload;
begin
  //ShowMessage('GetPostReturn URL >> '+URL);
  GetPostReturn(URL,ParaTs,Result);
end;

function GetXBJson(BaseUrl,MainFunctionName,SubFunctionName:string;ParaTs:TStrings):string;
begin
  Result:=GetPostReturn(GetFullURL(BaseUrl,MainFunctionName,SubFunctionName),ParaTs);
end;

//ParaStr 省去TStrings的麻煩 但一定要 Name Value Name Value 若Length為奇數 則 取消 返回
function GetXBJson(BaseUrl,MainFunctionName,SubFunctionName:string;ParaStr:array of String):string;
var ts:TStrings;
  I: Integer;
begin
  if Length(ParaStr) mod 2 = 1 then
  begin
    Showmessage('ParaStr Error');
    Exit;
  end;
  ts:=TStringList.Create;
  try
    for I := 0 to (Length(ParaStr) div 2)-1 do
      ts.Values[ParaStr[i*2]]:=ParaStr[i*2+1];
    //ShowMessage(ts.Text);
    Result:=GetXBJson(BaseUrl,MainFunctionName,SubFunctionName,ts);
  finally
    ts.Free;
  end;
end;

function GetXBJSonCDS(BaseURL,MainFunctionName,SubFunctionName:string;ParaTs:TStrings):TRestJsonCDS; //此函數將自動產生一個CDS 需自行釋放
begin
  Result:=TRestJsonCDS.Create(nil,GetFullURL(BaseURL,MainFunctionName,SubFunctionName),ParaTS);
end;

function GetXBJSonCDS(BaseURL,MainFunctionName,SubFunctionName:string;ParaStr:array of String):TRestJsonCDS; overload; //此函數將自動產生一個CDS 需自行釋放
var ts:TStrings;
  I: Integer;
begin
  if Length(ParaStr) mod 2 = 1 then
  begin
    Showmessage('ParaStr Error');
    Exit;
  end;
  ts:=TStringList.Create;
  try
    for I := 0 to (Length(ParaStr) div 2)-1 do
      ts.Values[ParaStr[i*2]]:=ParaStr[i*2+1];
    //ShowMessage(ts.Text);
    Result:=GetXBJSonCDS(BaseUrl,MainFunctionName,SubFunctionName,ts);
  finally
    ts.Free;
  end;
end;

function GetXBJsonOneField(BaseURL,MainFunctionName,SubFunctionName:string;ParaStr:array of String;FieldName:String):string;
begin
  with GetXBJSonCDS(BaseURL,MainFunctionName,SubFunctionName,ParaStr) do
  begin
    if FindField(FieldName)<>nil then
      Result:=FieldByName(FieldName).AsString
    else
    begin
      //ShowMessage('Field Not Found!');
      Result:=EmptyRecord;
    end;
    Free;
  end;
end;

{ TRestJsonCDS }

constructor TRestJsonCDS.Create(AOwner: TComponent;BaseURL:String;ParaTs:TStrings=nil);
var i:integer;
begin
  inherited Create(AOwner);
  RESTClient:=TRESTClient.Create(Self);
  RESTRequest:=TRESTRequest.Create(Self);
  RESTResponse:=TRESTResponse.Create(Self);
  RESTResponseDataSetAdapter:=TRESTResponseDataSetAdapter.Create(Self);

  RESTRequest.Client:=RESTClient;
  RESTRequest.Response:=RESTResponse;

  RESTResponseDataSetAdapter.Response:=RESTResponse;
  RESTResponseDataSetAdapter.Dataset:=Self;

  RESTClient.BaseURL:=BaseURL;

  if ParaTs<>nil then
  begin
    RESTRequest.Method := rmPOST;
    for I := 0 to ParaTs.Count-1 do
      RESTRequest.Params.AddItem(ParaTs.Names[i],ParaTs.ValueFromIndex[i]);
  end;

  RESTRequest.Execute;
end;

constructor TRestJsonCDS.Create(AOwner: TComponent; BaseURL: String;
  ParaStr: array of String);
var ts:TStrings;
  I: Integer;
begin
  if Length(ParaStr) mod 2 = 1 then
  begin
    Showmessage('ParaStr Error');
    Exit;
  end;
  ts:=TStringList.Create;
  try
    for I := 0 to (Length(ParaStr) div 2)-1 do
      ts.Values[ParaStr[i*2]]:=ParaStr[i*2+1];
    //ShowMessage(ts.Text);
    inherited Create(AOwner);
    RESTClient:=TRESTClient.Create(Self);
    RESTRequest:=TRESTRequest.Create(Self);
    RESTResponse:=TRESTResponse.Create(Self);
    RESTResponseDataSetAdapter:=TRESTResponseDataSetAdapter.Create(Self);

    RESTRequest.Client:=RESTClient;
    RESTRequest.Response:=RESTResponse;

    RESTResponseDataSetAdapter.Response:=RESTResponse;
    RESTResponseDataSetAdapter.Dataset:=Self;

    RESTClient.BaseURL:=BaseURL;

    RESTRequest.Method := rmPOST;
    for I := 0 to ts.Count-1 do
      RESTRequest.Params.AddItem(ts.Names[i],ts.ValueFromIndex[i]);

    RESTRequest.Execute;
  finally
    ts.Free;
  end;
end;

destructor TRestJsonCDS.destroy;
begin
  RESTResponseDataSetAdapter.Free;
  RESTResponse.Free;
  RESTRequest.Free;
  RESTClient.Free;
  inherited destroy;
end;

end.
 

附上檔案

https://github.com/Kamilia1470/XBJsonUnit.git

arrow
arrow
    文章標籤
    Delphi Json
    全站熱搜
    創作者介紹
    創作者 抓狂小白 的頭像
    抓狂小白

    抓狂小白的程式筆記

    抓狂小白 發表在 痞客邦 留言(0) 人氣()