//------------------------------------------------------------------------------ // 类名: TCslJson // // 描述: 简单的Json字符串解析。 // 为简化操作,该类对Json字符串的格式有一定的要求: // ①键名字不能重复;②键值全部是字符串。格式如下: // {"status":"true","msg":{"curr":"1","status2":"\u5ba1\u6838\u4e2d", // "name":"珠海纵横"}} // // 网上找过几个三方Json控件,基本不能用,有个叫lkJson的刚开始还不错,但 // 随着需求的深入,发现问题越来越多:不能嵌套数组、不能用索引的方式遍历 // 子结点(始终返回Null)、Delphi处理转义字符困难等问题。另外有些三方 // Json控件(如superobject1.24)只支持Delphi XE等高版本, 表现为有些语法在 // Delphi7中不支持、“Integer overflow”报错等。太多问题,无奈,自己写 // 个简单的解析。有点山寨,但能解决问题。有空再写个专业的。 // // 作者: Chenshilong, 2014-07-03 //------------------------------------------------------------------------------ unit CslJson; interface uses SysUtils, Classes, Dialogs; type TOVArr = array of array of string; // ObjectValuesArray TCslJson = class private FText: string; FOVArr: TOVArr; procedure SetText(const Value: string); function GetValue(const AField: string): string; // 处理转义字符的问题 function Transfer(AStr: string): string; public property Text: string read FText write SetText; // 取指定键名字的键值。如果键值是个对象则返回{Object} // 键值带不带"号都能正确解析,一律返回字符串类型。键值为空不影响结果, // 没有类型转换、溢出等问题。 property Value[const AField: string]: string read GetValue; // 如果指定键名字的键值是个对象,则把对象的每个属性值依次填入数组AArray中。 // AArray数组下标任意,数组元素个数任意(不会溢出) procedure ChildValues(AField: string; var AArray: array of string); function ArrayText(AField: string): string; // Json二维数组对象序列化后的字符串值 function ArrayElementCount(AField: string): Integer; // Json二维数组对象中元素个数 function ArrayElementText(AField: string; AIndex: Integer): string; // Json二维数组的第AIndex个元素的字符串值 function ArrayElementPropertyCount(AElementText: string): Integer; // Json二维数组的元素的键值对个数 function ArrayValues(AField: string): TOVArr; end; implementation { TcslJson } procedure TCslJson.SetText(const Value: string); begin FText := Value; end; function TCslJson.GetValue(const AField: string): string; var sF: string; iPos, iCur: Integer; begin Result := ''; if AField = '' then Exit; sF := Format('"%s":', [AField]); iPos := POS(sF, FText); if iPos = 0 then Exit; iCur := iPos + Length(sF); iPos := iCur; while (FText[iCur] <> ',') and (FText[iCur] <> '}') do Inc(iCur); Result := Copy(FText, iPos, iCur - iPos); if Pos('{', Result) > 0 then begin Result := '{Object}'; Exit; end; Result := StringReplace(Result, '"', '', [rfReplaceAll]); Result := Transfer(Result); end; procedure TCslJson.ChildValues(AField: string; var AArray: array of string); var sField, sText, sValue: string; iPos, iCur, i, j: Integer; vSL: TStringList; begin if AField = '' then Exit; sField := Format('"%s":', [AField]); iPos := POS(sField, FText); if iPos = 0 then Exit; iCur := iPos + Length(sField); iPos := iCur; while (FText[iCur] <> '}') do Inc(iCur); sText := Copy(FText, iPos, iCur - iPos); sText := StringReplace(sText, '{', '', []); // 去掉第一个"{" if Pos('{', sText) > 0 then // 证明还嵌套有子对象,不支持 begin AArray[Low(AArray)] := '{Contain Sub Object}'; Exit; end; // "curr":"1","status2":"\u5ba1\u6838\u4e2d","total":"1","name":"珠海纵横" vSL := TStringList.Create; try while Length(sText) > 0 do begin iPos := POS(':"', sText); iCur := iPos + 2; iPos := iCur; while (sText[iCur] <> '"') do Inc(iCur); sValue := Copy(sText, iPos, iCur - iPos); sValue := Transfer(sValue); vSL.Add(sValue); iCur := iCur + 2; sText := Copy(sText, iCur, Length(sText) - iCur + 1); end; // 这种方式保证数组下标任意,数组元素个数任意,不受限制 j := 1; for i := Low(AArray) to High(AArray) do begin if J > vSL.Count then Break; AArray[i] := vSL[j - 1]; Inc(j); end; finally vSL.Free; end; end; function TCslJson.Transfer(AStr: string): string; begin AStr := StringReplace(AStr, '\/', '\', [rfReplaceAll]); // 路径中的“\/” Result := AStr; end; function TCslJson.ArrayText(AField: string): string; var sField: string; iPos, iCur: Integer; begin Result := ''; if AField = '' then Exit; sField := Format('"%s":[', [AField]); iPos := POS(sField, FText); if iPos = 0 then Exit; iCur := iPos + Length(sField); iPos := iCur; while (FText[iCur] <> ']') do Inc(iCur); Result := Copy(FText, iPos, iCur - iPos); end; function TCslJson.ArrayElementText(AField: string; AIndex: Integer): string; var sAT: string; iLen, i, iCount, iPos1, iPos2: Integer; begin Result := ''; iCount := 0; iPos1 := 0; iPos2 := 0; sAT := ArrayText(AField); iLen := Length(sAT); for i := 1 to iLen do begin if sAT[i] = '{' then begin Inc(iCount); if iCount = AIndex then begin iPos1 := i; Break; end; end; end; for i := iPos1 to iLen do begin if sAT[i] = '}' then begin iPos2 := i; Break; end; end; Result := Copy(sAT, iPos1, iPos2 - iPos1 + 1); end; function TCslJson.ArrayElementCount(AField: string): Integer; var i, iLen: Integer; sAText: string; begin Result := 0; sAText := ArrayText(AField); iLen := Length(sAText); for i := 1 to iLen do begin if sAText[i] = '}' then Inc(Result); end; end; function TCslJson.ArrayElementPropertyCount(AElementText: string): Integer; var i, iLen: Integer; begin Result := 1; iLen := Length(AElementText); for i := 1 to iLen do begin if AElementText[i] = ',' then if (i < iLen) and (AElementText[i + 1] = '"') then Inc(Result); end; end; function TCslJson.ArrayValues(AField: string): TOVArr; var sText: string; iPos, iCur, i, j, iECount, iPCount: Integer; vSL: TStringList; begin Result := nil; if AField = '' then Exit; iECount := ArrayElementCount(AField); if iECount = 0 then Exit; iPCount := ArrayElementPropertyCount(ArrayElementText(AField, 1)); SetLength(FOVArr, iECount, iPCount); vSL := TStringList.Create; try for i := 1 to iECount do begin sText := ArrayElementText(AField, i); vSL.Clear; while Length(sText) > 0 do begin iPos := POS(':"', sText); iCur := iPos + 2; iPos := iCur; while (sText[iCur] <> '"') do Inc(iCur); vSL.Add(Transfer(Copy(sText, iPos, iCur - iPos))); iCur := iCur + 2; sText := Copy(sText, iCur, Length(sText) - iCur + 1); end; for j := 0 to vSL.Count - 1 do begin FOVArr[i - 1, j] := vSL[j]; end; end; finally vSL.Free; end; Result := FOVArr; end; end.