Comment découper une pièce dans un flux dans Delphi7
Question
Je reçois un message SOAP entrant sous la forme TStream (Delphi7), le serveur qui envoie ce savon est en mode de développement et ajoute un en-tête HTML au message à des fins de débogage. Maintenant, je dois couper la partie en-tête HTML avant de pouvoir la passer au convertisseur de savon. Il commence par le début avec la balise 'pre' et se termine par la balise '/ pre'. Je pense que cela devrait être assez facile mais je ne l’ai jamais fait auparavant dans Delphi7, alors quelqu'un peut-il m'aider?
La solution
Je pense que le code suivant ferait ce que vous voulez, en supposant que vous n’ayez qu’un < pre > bloquer dans votre document.
function DepreStream(Stm : tStream):tStream;
var
sTemp : String;
oStrStm : tStringStream;
i : integer;
begin
oStrStm := tStringStream.create('');
try
Stm.Seek(0,soFromBeginning);
oStrStm.copyfrom(Stm,Stm.Size);
sTemp := oStrStm.DataString;
if (Pos('<pre>',sTemp) > 0) and (Pos('</pre>',sTemp) > 0) then
begin
delete(sTemp,Pos('<pre>',sTemp),(Pos('</pre>',sTemp)-Pos('<pre>',sTemp))+6);
oStrStm.free;
oStrStm := tStringStream.Create(sTemp);
end;
Result := tMemoryStream.create;
oStrStm.Seek(0,soFromBeginning);
Result.CopyFrom(oStrStm,oStrStm.Size);
Result.Seek(0,soFromBeginning);
finally
oStrStm.free;
end;
end;
Je pense qu'une autre option serait d'utiliser une transformation XML pour supprimer les balises indésirables, mais je ne fais pas beaucoup de transformations, donc si quelqu'un d'autre veut ce flambeau ...
EDIT: corrigé le code pour qu'il fonctionne. M'apprend à coder directement dans SO plutôt que dans l'IDE en premier.
Autres conseils
Une autre solution, plus conforme à la suggestion de Lars et plus élaborée.
C'est plus rapide, surtout lorsque la taille du Stream est supérieure à 100, et encore plus pour les très gros. Cela évite de copier dans une chaîne intermédiaire.
FilterBeginStream est plus simple et respecte les & spécifications; & "; en supprimant tout jusqu'à la fin de l'en-tête.
FilterMiddleStream fait la même chose que DepreStream, laissant ce qui se trouve avant et après l'en-tête.
Avertissement : ce code concerne Delphi jusqu'à D2007 et non D2009.
// returns position of a string token (its 1st char) into a Stream. 0 if not found
function StreamPos(Token: string; AStream: TStream): Int64;
var
TokenLength: Integer;
StringToMatch: string;
begin
Result := 0;
TokenLength := Length(Token);
if TokenLength > 0 then
begin
SetLength(StringToMatch, TokenLength);
while AStream.Read(StringToMatch[1], 1) > 0 do
begin
if (StringToMatch[1] = Token[1]) and
((TokenLength = 1) or
((AStream.Read(StringToMatch[2], Length(Token)-1) = Length(Token)-1) and
(Token = StringToMatch))) then
begin
Result := AStream.Seek(0, soCurrent) - (Length(Token) - 1); // i.e. AStream.Position - (Length(Token) - 1);
Break;
end;
end;
end;
end;
// Returns portion of a stream after the end of a tag delimited header. Works for 1st header.
// Everything preceding the header is removed too. Returns same stream if no valid header detected.
// Result is True if valid header found and stream has been filtered.
function FilterBeginStream(const AStartTag, AEndTag: string; const AStreamIn, AStreamOut: TStream): Boolean;
begin
AStreamIn.Seek(0, soBeginning); // i.e. AStreamIn.Position := 0;
Result := (StreamPos(AStartTag, TStream(AStreamIn)) > 0) and (StreamPos(AEndTag, AStreamIn) > 0);
if Result then
AStreamOut.CopyFrom(AStreamIn, AStreamIn.Size - AStreamIn.Position)
else
AStreamOut.CopyFrom(AStreamIn, 0);
end;
// Returns a stream after removal of a tag delimited portion. Works for 1st encountered tag.
// Returns same stream if no valid tag detected.
// Result is True if valid tag found and stream has been filtered.
function FilterMiddleStream(const AStartTag, AEndTag: string; const AStreamIn, AStreamOut: TStream): Boolean;
var
StartPos, EndPos: Int64;
begin
Result := False;
AStreamIn.Seek(0, soBeginning); // i.e. AStreamIn.Position := 0;
StartPos := StreamPos(AStartTag, TStream(AStreamIn));
if StartPos > 0 then
begin
EndPos := StreamPos(AEndTag, AStreamIn);
Result := EndPos > 0;
end;
if Result then
begin
if StartPos > 1 then
begin
AStreamIn.Seek(0, soBeginning); // i.e. AStreamIn.Position := 0;
AStreamOut.CopyFrom(AStreamIn, StartPos - 1);
AStreamIn.Seek(EndPos - StartPos + Length(AEndTag), soCurrent);
end;
AStreamOut.CopyFrom(AStreamIn, AStreamIn.Size - AStreamIn.Position);
end
else
AStreamOut.CopyFrom(AStreamIn, 0);
end;
Créez un nouveau TStream (utilisez TMemoryStream) et déplacez tous les éléments que vous souhaitez conserver d'un flux à l'autre avec les méthodes TStream.CopyFrom ou TStream.ReadBuffer / WriteBuffer.
Une expression XPath de " //pre[1][1]
" va sortir le premier noeud du premier < pre > balise dans le message XML: à partir de votre description, elle doit contenir le message SOAP souhaité.
Cela fait plusieurs années que je ne l'utilise pas pour la dernière fois, mais je pense que la bibliothèque OpenXML de Dieter Koehler prend en charge XPath.