📄 jvsimplexml.pas
字号:
end;
constructor TJvSimpleXMLProps.Create(Parent: TJvSimpleXMLElem);
begin
inherited Create;
FParent := Parent;
end;
procedure TJvSimpleXMLProps.Delete(const Name: string);
begin
if FProperties <> nil then
Delete(FProperties.IndexOf(Name));
end;
destructor TJvSimpleXMLProps.Destroy;
begin
FParent := nil;
Clear;
FreeAndNil(FProperties);
inherited Destroy;
end;
procedure TJvSimpleXMLProps.DoItemRename(var Value: TJvSimpleXMLProp;
const Name: string);
var
I: Integer;
begin
if FProperties = nil then
Exit;
I := FProperties.IndexOfObject(Value);
if I <> -1 then
FProperties[I] := Name;
end;
procedure TJvSimpleXMLProps.Error(const S: string);
begin
raise EJvSimpleXMLError.Create(S);
end;
procedure TJvSimpleXMLProps.FmtError(const S: string;
const Args: array of const);
begin
Error(Format(S, Args));
end;
function TJvSimpleXMLProps.GetCount: Integer;
begin
if FProperties = nil then
Result := 0
else
Result := FProperties.Count;
end;
function TJvSimpleXMLProps.GetItem(const Index: Integer): TJvSimpleXMLProp;
begin
if FProperties <> nil then
Result := TJvSimpleXMLProp(FProperties.Objects[Index])
else
Result := nil;
end;
function TJvSimpleXMLProps.GetItemNamedDefault(const Name, Default: string): TJvSimpleXMLProp;
var
I: Integer;
begin
Result := nil;
if FProperties <> nil then
begin
I := FProperties.IndexOf(Name);
if I <> -1 then
Result := TJvSimpleXMLProp(FProperties.Objects[I])
else
if Assigned(FParent) and Assigned(FParent.SimpleXML) and (sxoAutoCreate in FParent.SimpleXML.Options) then
Result := Add(Name, Default);
end
else
if Assigned(FParent) and Assigned(FParent.SimpleXML) and (sxoAutoCreate in FParent.SimpleXML.Options) then
begin
Result := Add(Name, Default);
end;
end;
function TJvSimpleXMLProps.GetItemNamed(const Name: string): TJvSimpleXMLProp;
begin
Result := GetItemNamedDefault(Name, '');
end;
function TJvSimpleXMLProps.GetSimpleXML: TJvSimpleXML;
begin
if FParent <> nil then
Result := FParent.GetSimpleXML
else
Result := nil;
end;
function TJvSimpleXMLProps.IntValue(const Name: string; Default: Int64): Int64;
var
Prop: TJvSimpleXMLProp;
begin
Prop := GetItemNamedDefault(Name, IntToStr(Default));
if Prop = nil then
Result := Default
else
Result := Prop.IntValue;
end;
procedure TJvSimpleXMLProps.LoadFromStream(const Stream: TStream);
//<element Prop="foo" Prop='bar' foo:bar="beuh"/>
//Stop on / or ? or >
type
TPosType = (
ptWaiting,
ptReadingName,
ptStartingContent,
ptReadingValue,
ptSpaceBeforeEqual
);
var
lPos: TPosType;
I, lStreamPos, Count: Integer;
lBuf: array [0..cBufferSize - 1] of Char;
lName, lValue, lNameSpace: string;
lPropStart: Char;
begin
lStreamPos := Stream.Position;
lValue := '';
lNameSpace := '';
lName := '';
lPropStart := ' ';
lPos := ptWaiting;
// We read from a stream, thus replacing the existing properties
Clear;
repeat
Count := Stream.Read(lBuf, SizeOf(lBuf));
for I := 0 to Count - 1 do
begin
//Increment Stream pos for after comment
Inc(lStreamPos);
case lPos of
ptWaiting: //We are waiting for a property
begin
case lBuf[I] of
' ', Tab, Cr, Lf:
begin
end;
'a'..'z', 'A'..'Z', '0'..'9', '-', '_':
begin
lName := lBuf[I];
lNameSpace := '';
lPos := ptReadingName;
end;
'/', '>', '?':
begin
Dec(lStreamPos);
Count := 0;
Break;
end;
else
FmtError(RsEInvalidXMLElementUnexpectedCharacte, [lBuf[I]]);
end;
end;
ptReadingName: //We are reading a property name
case lBuf[I] of
'a'..'z', 'A'..'Z', '0'..'9', '-', '_':
lName := lName + lBuf[I];
':':
begin
lNameSpace := lName;
lName := '';
end;
'=':
lPos := ptStartingContent;
' ', Tab, Cr, Lf:
lPos := ptSpaceBeforeEqual;
else
FmtError(RsEInvalidXMLElementUnexpectedCharacte, [lBuf[I]]);
end;
ptStartingContent: //We are going to start a property content
case lBuf[I] of
' ', Tab, Cr, Lf:
; // ignore white space
'''', '"':
begin
lPropStart := lBuf[I];
lValue := '';
lPos := ptReadingValue;
end;
else
FmtError(RsEInvalidXMLElementUnexpectedCharacte_, [lBuf[I]]);
end;
ptReadingValue: //We are reading a property
if lBuf[I] = lPropStart then
begin
if (GetSimpleXML <> nil) then
GetSimpleXML.DoDecodeValue(lValue);
with Add(lName, lValue) do
NameSpace := lNameSpace;
lPos := ptWaiting;
end
else
lValue := lValue + lBuf[I];
ptSpaceBeforeEqual: // We are reading the white space between a property name and the = sign
case lBuf[I] of
' ', Tab, Cr, Lf:
; // more white space, stay in this state and ignore
'=':
lPos := ptStartingContent;
else
FmtError(RsEInvalidXMLElementUnexpectedCharacte, [lBuf[I]]);
end;
else
Assert(False, RsEUnexpectedValueForLPos);
end;
end;
until Count = 0;
Stream.Seek(lStreamPos, soFromBeginning);
end;
procedure TJvSimpleXMLProps.SaveToStream(const Stream: TStream);
var
St: string;
I: Integer;
begin
St := '';
for I := 0 to Count - 1 do
St := St + Item[I].SaveToString;
if St <> '' then
Stream.Write(St[1], Length(St));
end;
function TJvSimpleXMLProps.Value(const Name: string; Default: string): string;
var
Prop: TJvSimpleXMLProp;
begin
Result := '';
Prop := GetItemNamedDefault(Name, Default);
if Prop = nil then
Result := Default
else
Result := Prop.Value;
end;
//=== { TJvSimpleXMLProp } ===================================================
function TJvSimpleXMLProp.GetBoolValue: Boolean;
begin
Result := StrToBoolDef(Value, False);
end;
function TJvSimpleXMLProp.GetFloatValue: Extended;
begin
Result := StrToFloatDef(Value, 0.0);
end;
function TJvSimpleXMLProp.FullName: string;
begin
if FNameSpace <> '' then
Result := FNameSpace + ':' + Name
else
Result := Name;
end;
function TJvSimpleXMLProp.GetIntValue: Int64;
begin
Result := StrToInt64Def(Value, -1);
end;
function TJvSimpleXMLProp.GetSimpleXML: TJvSimpleXML;
begin
if (FParent <> nil) and (FParent.FParent <> nil) then
Result := FParent.FParent.GetSimpleXML
else
Result := nil;
end;
function TJvSimpleXMLProp.SaveToString: string;
var
AEncoder: TJvSimpleXML;
tmp:string;
begin
AEncoder := GetSimpleXML;
tmp := FValue;
if NameSpace <> '' then
begin
if AEncoder <> nil then
AEncoder.DoEncodeValue(tmp);
Result := Format(' %s:%s="%s"', [NameSpace, Name, tmp]);
end
else
begin
if AEncoder <> nil then
AEncoder.DoEncodeValue(tmp);
Result := Format(' %s="%s"', [Name, tmp]);
end;
end;
procedure TJvSimpleXMLProp.SetBoolValue(const Value: Boolean);
begin
FValue := BoolToStr(Value);
end;
procedure TJvSimpleXMLProp.SetFloatValue(const Value: Extended);
begin
FValue := FloatToStr(Value);
end;
procedure TJvSimpleXMLProp.SetIntValue(const Value: Int64);
begin
FValue := IntToStr(Value);
end;
procedure TJvSimpleXMLProp.SetName(const Value: string);
begin
if (Value <> FName) and (Value <> '') then
begin
if (Parent <> nil) and (FName <> '') then
Parent.DoItemRename(Self, Value);
FName := Value;
end;
end;
//=== { TJvSimpleXMLElemClassic } ============================================
procedure TJvSimpleXMLElemClassic.LoadFromStream(const Stream: TStream; Parent: TJvSimpleXML);
//<element Prop="foo" Prop='bar'/>
//<element Prop="foo" Prop='bar'>foor<b>beuh</b>bar</element>
//<xml:element Prop="foo" Prop='bar'>foor<b>beuh</b>bar</element>
var
I, lStreamPos, Count, lPos: Integer;
lBuf: array [0..cBufferSize - 1] of Char;
St, lName, lValue, lNameSpace: string;
begin
lStreamPos := Stream.Position;
St := '';
lValue := '';
lNameSpace := '';
lPos := 1;
repeat
Count := Stream.Read(lBuf, SizeOf(lBuf));
if Parent <> nil then
Parent.DoLoadProgress(Stream.Position, Stream.Size);
for I := 0 to Count - 1 do
begin
//Increment Stream pos for after comment
Inc(lStreamPos);
case lPos of
1:
if lBuf[I] = '<' then
lPos := 2
else
FmtError(RsEInvalidXMLElementExpectedBeginningO, [lBuf[I]]);
-1:
if lBuf[I] = '>' then
begin
Count := 0;
Break;
end
else
FmtError(RsEInvalidXMLElementExpectedEndOfTagBu, [lBuf[I]]);
else
begin
if lBuf[I] in [Tab, Lf, Cr, ' ' {, '.'}] then
begin
if lPos = 2 then
Error(RsEInvalidXMLElementMalformedTagFoundn);
Stream.Seek(lStreamPos, soFromBeginning);
Properties.LoadFromStream(Stream);
lStreamPos := Stream.Position;
Break; //Re read buffer
end
else
begin
case lBuf[I] of
'>':
begin
lName := St;
//Load elements
Stream.Seek(lStreamPos, soFromBeginning);
St := Items.LoadFromStream(Stream, Parent);
if lNameSpace <> '' then
begin
if not AnsiSameText(lNameSpace + ':' + lName, St) then
FmtError(RsEInvalidXMLElementErroneousEndOfTagE, [lName, St]);
end
else
if not AnsiSameText(lName, St) then
FmtError(RsEInvalidXMLElementErroneousEndOfTagE, [lName, St]);
lStreamPos := Stream.Position;
//Set value if only one sub element
//This might reduce speed, but this is for compatibility issues
if (Items.Count = 1) and (Items[0] is TJvSimpleXMLElemText) then
begin
lValue := Items[0].Value;
Items.Clear;
end;
Count := 0;
Break;
end;
'/':
begin
lName := St;
lPos := -1;
end;
':':
begin
lNameSpace := St;
St := '';
end;
else
begin
St := St + lBuf[I];
Inc(lPos);
end;
end;
end;
end;
end;
end;
until Count = 0;
Name := lName;
if GetSimpleXML <> nil then
GetSimpleXML.DoDecodeValue(lValue);
Value := lValue;
NameSpace := lNameSpace;
if Parent <> nil then
begin
Parent.DoTagParsed(lName);
Parent.DoValueParsed(lName, lValue);
end;
Stream.Seek(lStreamPos, soFromBeginning);
end;
procedure TJvSimpleXMLElemClassic.SaveToStream(const Stream: TStream; const Level: string; Parent: TJvSimpleXML);
var
St, AName, tmp: string;
LevelAdd: string;
begin
if(NameSpace <> '') then
begin
AName := NameSpace + ':' + Name;
end
else
begin
AName
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -