📄 jvqsimplexml.pas
字号:
const
cBufferSize = 8192;
DefaultTrueBoolStr = 'True'; // DO NOT LOCALIZE
DefaultFalseBoolStr = 'False'; // DO NOT LOCALIZE
var
GlobalSorts: TList = nil;
GlobalXMLVariant: TXMLVariant = nil;
function GSorts: TList;
begin
if not Assigned(GlobalSorts) then
GlobalSorts := TList.Create;
Result := GlobalSorts;
end;
function XMLVariant: TXMLVariant;
begin
if not Assigned(GlobalXMLVariant) then
GlobalXMLVariant := TXMLVariant.Create;
Result := GlobalXMLVariant;
end;
function EntityEncode(const S: string): string;
var
I, J, K, L: Integer;
tmp: string;
begin
SetLength(Result, Length(S) * 6); // worst case
J := 1;
I := 1;
L := Length(S);
while I <= L do
begin
case S[I] of
'"':
tmp := '"';
'&':
tmp := '&';
#39:
tmp := ''';
'<':
tmp := '<';
'>':
tmp := '>';
else
tmp := S[I];
end;
for K := 1 to Length(tmp) do
begin
Result[J] := tmp[K];
Inc(J);
end;
Inc(I);
end;
if J > 1 then
SetLength(Result, J - 1)
else
SetLength(Result, 0);
end;
function EntityDecode(const S: string): string;
var
I, J, L: Integer;
begin
Result := S;
I := 1;
J := 1;
L := Length(Result);
while I <= L do
begin
if Result[I] = '&' then
begin
if AnsiSameText(Copy(Result, I, 5), '&') then
begin
Result[J] := '&';
Inc(J);
Inc(I, 4);
end
else
if AnsiSameText(Copy(Result, I, 4), '<') then
begin
Result[J] := '<';
Inc(J);
Inc(I, 3);
end
else
if AnsiSameText(Copy(Result, I, 4), '>') then
begin
Result[J] := '>';
Inc(J);
Inc(I, 3);
end
else
if AnsiSameText(Copy(Result, I, 6), ''') then
begin
Result[J] := #39;
Inc(J);
Inc(I, 5);
end
else
if AnsiSameText(Copy(Result, I, 6), '"') then
begin
Result[J] := '"';
Inc(J);
Inc(I, 5);
end
else
begin
Result[J] := Result[I];
Inc(J);
end;
end
else
begin
Result[J] := Result[I];
Inc(J);
end;
Inc(I);
end;
if J > 1 then
SetLength(Result, J - 1)
else
SetLength(Result, 0);
end;
function SimpleXMLEncode(const S: string): string;
const
NoConversion = [#0..#127] - ['"', '&', #39, '<', '>'];
var
I, J, K: Integer;
tmp: string;
begin
SetLength(Result, Length(S) * 6); // worst case
J := 1;
for I := 1 to Length(S) do
begin
if S[I] in NoConversion then
Result[J] := S[I]
else
begin
case S[I] of
'"':
tmp := '"';
'&':
tmp := '&';
#39:
tmp := ''';
'<':
tmp := '<';
'>':
tmp := '>';
else
tmp := Format('&#x%.2x;', [Ord(S[I])]);
end;
for K := 1 to Length(tmp) do
begin
Result[J] := tmp[K];
Inc(J);
end;
Dec(J);
end;
Inc(J);
end;
if J > 0 then
SetLength(Result, J - 1)
else
SetLength(Result, 0);
end;
procedure SimpleXMLDecode(var S: string; TrimBlanks: Boolean);
var
StringLength, ReadIndex, WriteIndex: Cardinal;
procedure DecodeEntity(var S: string; StringLength: Cardinal;
var ReadIndex, WriteIndex: Cardinal);
const
cHexPrefix: array [Boolean] of PChar = ('', '$');
var
I: Cardinal;
Value: Integer;
IsHex: Boolean;
begin
Inc(ReadIndex, 2);
IsHex := (ReadIndex <= StringLength) and (S[ReadIndex] in ['x', 'X']);
Inc(ReadIndex, Ord(IsHex));
I := ReadIndex;
while ReadIndex <= StringLength do
begin
if S[ReadIndex] = ';' then
begin
Value := StrToIntDef(cHexPrefix[IsHex] + Copy(S, I, ReadIndex - I), -1); // no characters are less than 0
if Value > 0 then
S[WriteIndex] := Chr(Value)
else
ReadIndex := I - (2 + Cardinal(IsHex)); // reset to start
Exit;
end;
Inc(ReadIndex);
end;
ReadIndex := I - (2 + Cardinal(IsHex)); // reset to start
end;
procedure SkipBlanks(var S: string; StringLength: Cardinal; var ReadIndex: Cardinal);
begin
while ReadIndex < StringLength do
begin
if S[ReadIndex] = Cr then
S[ReadIndex] := Lf
else
if S[ReadIndex + 1] = Cr then
S[ReadIndex + 1] := Lf;
if (S[ReadIndex] < #33) and (S[ReadIndex] = S[ReadIndex + 1]) then
Inc(ReadIndex)
else
Exit;
end;
end;
begin
// NB! This procedure replaces the text inplace to speed up the conversion. This
// works because when decoding, the string can only become shorter. This is
// accomplished by keeping track of the current read and write points.
// In addition, the original string length is read only once and passed to the
// inner procedures to speed up conversion as much as possible
ReadIndex := 1;
WriteIndex := 1;
StringLength := Length(S);
while ReadIndex <= StringLength do
begin
// this call lowers conversion speed by ~30%, ie 21MB/sec -> 15MB/sec (repeated tests, various inputs)
if TrimBlanks then
SkipBlanks(S, StringLength, ReadIndex);
if S[ReadIndex] = '&' then
begin
if S[ReadIndex + 1] = '#' then
begin
DecodeEntity(S, StringLength, ReadIndex, WriteIndex);
Inc(WriteIndex);
end
else
if AnsiSameText(Copy(S, ReadIndex, 5), '&') then
begin
S[WriteIndex] := '&';
Inc(WriteIndex);
Inc(ReadIndex, 4);
end
else
if AnsiSameText(Copy(S, ReadIndex, 4), '<') then
begin
S[WriteIndex] := '<';
Inc(WriteIndex);
Inc(ReadIndex, 3);
end
else
if AnsiSameText(Copy(S, ReadIndex, 4), '>') then
begin
S[WriteIndex] := '>';
Inc(WriteIndex);
Inc(ReadIndex, 3);
end
else
if AnsiSameText(Copy(S, ReadIndex, 6), ''') then
begin
S[WriteIndex] := #39;
Inc(WriteIndex);
Inc(ReadIndex, 5);
end
else
if AnsiSameText(Copy(S, ReadIndex, 6), '"') then
begin
S[WriteIndex] := '"';
Inc(WriteIndex);
Inc(ReadIndex, 5);
end
else
begin
S[WriteIndex] := S[ReadIndex];
Inc(WriteIndex);
end;
end
else
begin
S[WriteIndex] := S[ReadIndex];
Inc(WriteIndex);
end;
Inc(ReadIndex);
end;
if WriteIndex > 0 then
SetLength(S, WriteIndex - 1)
else
SetLength(S, 0);
// this call lowers conversion speed by ~65%, ie 21MB/sec -> 7MB/sec (repeated tests, various inputs)
// if TrimBlanks then
// S := AdjustLineBreaks(S);
end;
function XMLEncode(const S: string): string;
begin
Result := SimpleXMLEncode(S);
end;
function XMLDecode(const S: string): string;
begin
Result := S;
SimpleXMLDecode(Result, False);
end;
//=== { TJvSimpleXML } =======================================================
constructor TJvSimpleXML.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FRoot := TJvSimpleXMLElemClassic.Create(nil);
FRoot.FSimpleXML := Self;
FProlog := TJvSimpleXMLElemsProlog.Create;
FOptions := [sxoAutoIndent, sxoAutoEncodeValue, sxoAutoEncodeEntity];
FIndentString := ' ';
end;
destructor TJvSimpleXML.Destroy;
begin
FreeAndNil(FRoot);
FreeAndNil(FProlog);
inherited Destroy;
end;
procedure TJvSimpleXML.DoDecodeValue(var Value: string);
begin
if Assigned(FOnDecodeValue) then
FOnDecodeValue(Self, Value)
else
if sxoAutoEncodeValue in Options then
SimpleXMLDecode(Value, False)
else
if sxoAutoEncodeEntity in Options then
Value := EntityDecode(Value);
end;
procedure TJvSimpleXML.DoEncodeValue(var Value: string);
begin
if Assigned(FOnEncodeValue) then
FOnEncodeValue(Self, Value)
else
if sxoAutoEncodeValue in Options then
Value := SimpleXMLEncode(Value)
else
if sxoAutoEncodeEntity in Options then
Value := EntityEncode(Value);
end;
procedure TJvSimpleXML.DoLoadProgress(const APosition, ATotal: Integer);
begin
if Assigned(FOnLoadProg) then
FOnLoadProg(Self, APosition, ATotal);
end;
procedure TJvSimpleXML.DoSaveProgress;
begin
if Assigned(FOnSaveProg) then
begin
Inc(FSaveCount);
FOnSaveProg(Self, FSaveCurrent, FSaveCount);
end;
end;
procedure TJvSimpleXML.DoTagParsed(const AName: string);
begin
if Assigned(FOnTagParsed) then
FOnTagParsed(Self, AName);
end;
procedure TJvSimpleXML.DoValueParsed(const AName, AValue: string);
begin
if Assigned(FOnValue) then
FOnValue(Self, AName, AValue);
end;
procedure TJvSimpleXML.LoadFromFile(const FileName: TFileName);
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile(FileName);
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TJvSimpleXML.LoadFromResourceName(Instance: THandle;
const ResName: string);
const
RT_RCDATA = PChar(10);
var
Stream: TResourceStream;
begin
Stream := TResourceStream.Create(Instance, ResName, RT_RCDATA);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TJvSimpleXML.LoadFromStream(Stream: TStream);
var
AOutStream: TStream;
DoFree: Boolean;
begin
FRoot.Clear;
FProlog.Clear;
AOutStream := nil;
DoFree := False;
try
if Assigned(FOnDecodeStream) then
begin
AOutStream := TMemoryStream.Create;
DoFree := True;
FOnDecodeStream(Self, Stream, AOutStream);
AOutStream.Seek(0, soFromBeginning);
end
else
AOutStream := Stream;
if Assigned(FOnLoadProg) then
begin
FOnLoadProg(Self, AOutStream.Position, AOutStream.Size);
// Read doctype and so on
FProlog.LoadFromStream(AOutStream, Self);
// Read elements
FRoot.LoadFromStream(AOutStream, Self);
FOnLoadProg(Self, AOutStream.Position, AOutStream.Size);
end
else
begin
if Assigned(FOnTagParsed) or Assigned(FOnValue) then
begin
FProlog.LoadFromStream(AOutStream, Self);
FRoot.LoadFromStream(AOutStream, Self);
end
else
begin
FProlog.LoadFromStream(AOutStream);
FRoot.LoadFromStream(AOutStream);
end;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -