应群友的要求,编写了一个解析 DFM 文件格式,将其转换为 JSON 格式的函数,需要引用 QJSON 和 QString 单元。代码分享给大家,供大家参考:
function DFM2Json(AFileName: String): TQJson; var ADFMStream, ATemp: TMemoryStream; I: Integer; AObjNode: TQJson; const SSpace: PWideChar = ' '#9; SColon: PWideChar = ':'; SNull: WideChar = #0; function IsTextDFM(S: PAnsiChar): Boolean; begin Result := (AnsiStrLComp(S, 'object', 6) = 0) or (AnsiStrLComp(S, 'inherited', 9) = 0); end; procedure DecodeObject(ALine: PWideChar; var AObjName, AObjClass: QStringW); begin SkipUntilW(ALine, SSpace); AObjName := Trim(DecodeTokenW(ALine, SColon, SNull, True)); AObjClass := Trim(ALine); end; function DecodeDFMString(S: QStringW): QStringW; var ps, pd: PWideChar; V: Int64; begin if Length(S) > 0 then begin ps := PWideChar(S); SetLength(Result, Length(S)); pd := PWideChar(Result); while ps^ <> #0 do begin if ps^ = '#' then begin Inc(ps); if ParseInt(ps, V) > 0 then begin pd^ := WideChar(V); Inc(pd); end; end else if ps^ = '''' then begin Inc(ps); while ps^ <> '''' do begin pd^ := ps^; Inc(pd); Inc(ps); end; Inc(ps); end else begin break; end; end; SetLength(Result, pd - PWideChar(Result)); end else Result := ''; end; procedure DecodePropAndChildren(AParent: TQJson; var ps: PWideChar; AIsCollection: Boolean); var ALine, AName, AValue: QStringW; pl, pv: PWideChar; ATemp: TQJson; ACharset: Longint; AProps: TQJson; AChildren: TQJson; const SObject: PWideChar = 'object'; SInline: PWideChar = 'inline'; SEnd: PWideChar = 'end'; SEqual: PWideChar = '='; SNull: WideChar = #0; SItem: PWideChar = 'item'; SBracket: PWideChar = ')'; begin AProps := AParent; AChildren := nil; repeat ALine := DecodeLineW(ps); pl := PWideChar(ALine); SkipSpaceW(pl); SkipSpaceW(ps); if StartWithW(pl, SObject, True) or StartWithW(pl, SInline, True) then begin DecodeObject(pl, AName, AValue); DecodePropAndChildren(AParent.Add(AName, jdtObject), ps, False); end else if StartWithW(pl, SEnd, True) then Exit else // Property line begin AName := Trim(DecodeTokenW(pl, SEqual, SNull, True)); AValue := Trim(pl); pv := PWideChar(AValue); if (pv^ = '''') or (pv^ = '#') then begin AProps.ForcePath(AName).AsString := DecodeDFMString(AValue); end else if pv^ = '<' then begin Inc(pv); SkipSpaceW(pv); if pv^ <> '>' then begin ATemp := TQJson.Create; ATemp.DataType := jdtArray; try SkipSpaceW(ps); while StartWithW(ps, SItem, True) do begin SkipLineW(ps); DecodePropAndChildren(ATemp.Add, ps, True); end; SkipSpaceW(ps); if ps^ = '>' then SkipLineW(ps); finally if ATemp.Count > 0 then AProps.Add(AName).Assign(ATemp); FreeObject(ATemp); end; end; end else if pv^ = '(' then // Strings begin AValue := ''; while ps^ <> #0 do begin ALine := Trim(DecodeLineW(ps)); if Length(AValue) > 0 then AValue := AValue + SLineBreak + DecodeDFMString(ALine) else AValue := DecodeDFMString(ALine); if (ps^ = ')') or EndWithW(ALine, SBracket, True) then break; end; SkipSpaceW(ps); if ps^ = ')' then SkipLineW(ps); SkipSpaceW(ps); if Length(AValue) > 0 then AProps.ForcePath(AName).AsString := AValue; end else AProps.ForcePath(AName).Value := AValue; end; until Length(ALine) = 0; end; procedure DoConvert(AText: QStringW); var p: PWideChar; AObjectName, ARootClass: QStringW; const SObject: PWideChar = 'object '; SInherited: PWideChar = 'inherited '; begin if Length(AText) > 0 then begin p := PWideChar(AText); if StartWithW(p, SObject, True) or StartWithW(p, SInherited, True) then begin Result := TQJson.Create; try DecodeObject(PWideChar(DecodeLineW(p)), AObjectName, ARootClass); DecodePropAndChildren(Result.Add(ARootClass, jdtObject), p, False); finally if Result.Count = 0 then FreeAndNil(Result); end; end; end; end; begin ADFMStream := TMemoryStream.Create; try ADFMStream.LoadFromFile(AFileName); if PCardinal(ADFMStream.Memory)^ = $30465054 then begin ATemp := TMemoryStream.Create; ATemp.CopyFrom(ADFMStream, 0); ATemp.Position := 0; ADFMStream.Size := 0; ObjectBinaryToText(ATemp, ADFMStream); FreeAndNil(ATemp); ADFMStream.Position := 0; DoConvert(LoadTextW(ADFMStream)); end else if IsTextDFM(ADFMStream.Memory) then DoConvert(LoadTextW(ADFMStream)); finally FreeObject(ADFMStream); end; end;
用法:
procedure TForm1.Button1Click(Sender: TObject); var AJson: TQJson; begin if OpenDialog1.Execute then begin AJson := DFM2Json(OpenDialog1.FileName); if AJson <> nil then begin Memo1.Lines.Text := AJson.AsString; FreeAndNil(AJson); end; end; end;
一个转换结果示例:
{ "TForm1":{ "Left":446, "Top":185, "Caption":"Form1", "ClientHeight":394, "ClientWidth":545, "Color":"clBtnFace", "Font":{ "Charset":"DEFAULT_CHARSET", "Color":"clWindowText", "Height":-11, "Name":"Tahoma", "Style":[] }, "OldCreateOrder":false, "PixelsPerInch":96, "TextHeight":13, "Label1":{ "Left":288, "Top":64, "Width":75, "Height":20, "Caption":"测试LAB", "Font":{ "Charset":"GB2312_CHARSET", "Color":"clBlue", "Height":-20, "Name":"宋体", "Style":"[fsBold]" }, "ParentFont":false }, "Edit1":{ "Left":16, "Top":56, "Width":169, "Height":29, "Font":{ "Charset":"GB2312_CHARSET", "Color":"clWindowText", "Height":-21, "Name":"宋体", "Style":"[fsBold]" }, "ParentFont":false, "TabOrder":0, "Text":"Edit1" } } }