xmlwrite.pp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. {
  2. This file is part of the Free Component Library
  3. XML writing routines
  4. Copyright (c) 1999-2000 by Sebastian Guenther, [email protected]
  5. Modified in 2006 by Sergei Gorelkin, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. unit XMLWrite;
  13. {$ifdef fpc}{$MODE objfpc}{$endif}
  14. {$H+}
  15. interface
  16. uses Classes, DOM;
  17. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String); overload;
  18. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text); overload;
  19. procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream); overload;
  20. procedure WriteXML(Element: TDOMNode; const AFileName: String); overload;
  21. procedure WriteXML(Element: TDOMNode; var AFile: Text); overload;
  22. procedure WriteXML(Element: TDOMNode; AStream: TStream); overload;
  23. // ===================================================================
  24. implementation
  25. uses SysUtils, xmlutils;
  26. type
  27. TSpecialCharCallback = procedure(c: WideChar) of object;
  28. TXMLWriter = class(TObject)
  29. private
  30. FInsideTextNode: Boolean;
  31. FIndent: WideString;
  32. FIndentCount: Integer;
  33. FBuffer: PChar;
  34. FBufPos: PChar;
  35. FCapacity: Integer;
  36. FLineBreak: string;
  37. FNSHelper: TNSSupport;
  38. FScratch: TFPList;
  39. procedure wrtChars(Src: PWideChar; Length: Integer);
  40. procedure IncIndent;
  41. procedure DecIndent; {$IFDEF HAS_INLINE} inline; {$ENDIF}
  42. procedure wrtStr(const ws: WideString); {$IFDEF HAS_INLINE} inline; {$ENDIF}
  43. procedure wrtChr(c: WideChar); {$IFDEF HAS_INLINE} inline; {$ENDIF}
  44. procedure wrtIndent; {$IFDEF HAS_INLINE} inline; {$ENDIF}
  45. procedure wrtQuotedLiteral(const ws: WideString);
  46. procedure ConvWrite(const s: WideString; const SpecialChars: TSetOfChar;
  47. const SpecialCharCallback: TSpecialCharCallback);
  48. procedure AttrSpecialCharCallback(c: WideChar);
  49. procedure TextNodeSpecialCharCallback(c: WideChar);
  50. procedure WriteNSDef(B: TBinding);
  51. procedure NamespaceFixup(Element: TDOMElement);
  52. protected
  53. procedure Write(const Buffer; Count: Longint); virtual; abstract;
  54. procedure WriteNode(Node: TDOMNode);
  55. procedure VisitDocument(Node: TDOMNode);
  56. procedure VisitElement(Node: TDOMNode);
  57. procedure VisitText(Node: TDOMNode);
  58. procedure VisitCDATA(Node: TDOMNode);
  59. procedure VisitComment(Node: TDOMNode);
  60. procedure VisitFragment(Node: TDOMNode);
  61. procedure VisitAttribute(Node: TDOMNode);
  62. procedure VisitEntityRef(Node: TDOMNode);
  63. procedure VisitDocumentType(Node: TDOMNode);
  64. procedure VisitPI(Node: TDOMNode);
  65. public
  66. constructor Create;
  67. destructor Destroy; override;
  68. end;
  69. TTextXMLWriter = Class(TXMLWriter)
  70. Private
  71. F : ^Text;
  72. Protected
  73. Procedure Write(Const Buffer; Count : Longint);override;
  74. Public
  75. constructor Create(var AFile: Text);
  76. end;
  77. TStreamXMLWriter = Class(TXMLWriter)
  78. Private
  79. F : TStream;
  80. Protected
  81. Procedure Write(Const Buffer; Count : Longint);override;
  82. Public
  83. constructor Create(AStream: TStream);
  84. end;
  85. { ---------------------------------------------------------------------
  86. TTextXMLWriter
  87. ---------------------------------------------------------------------}
  88. constructor TTextXMLWriter.Create(var AFile: Text);
  89. begin
  90. inherited Create;
  91. f := @AFile;
  92. end;
  93. procedure TTextXMLWriter.Write(const Buffer; Count: Longint);
  94. var
  95. s: string;
  96. begin
  97. if Count>0 then
  98. begin
  99. SetString(s, PChar(@Buffer), Count);
  100. system.Write(f^, s);
  101. end;
  102. end;
  103. { ---------------------------------------------------------------------
  104. TStreamXMLWriter
  105. ---------------------------------------------------------------------}
  106. constructor TStreamXMLWriter.Create(AStream: TStream);
  107. begin
  108. inherited Create;
  109. F := AStream;
  110. end;
  111. procedure TStreamXMLWriter.Write(const Buffer; Count: Longint);
  112. begin
  113. if Count > 0 then
  114. F.Write(Buffer, Count);
  115. end;
  116. { ---------------------------------------------------------------------
  117. TXMLWriter
  118. ---------------------------------------------------------------------}
  119. constructor TXMLWriter.Create;
  120. var
  121. I: Integer;
  122. begin
  123. inherited Create;
  124. // some overhead - always be able to write at least one extra UCS4
  125. FBuffer := AllocMem(512+32);
  126. FBufPos := FBuffer;
  127. FCapacity := 512;
  128. // Initialize Indent string
  129. SetLength(FIndent, 100);
  130. FIndent[1] := #10;
  131. for I := 2 to 100 do FIndent[I] := ' ';
  132. FIndentCount := 0;
  133. // Later on, this may be put under user control
  134. // for now, take OS setting
  135. FLineBreak := sLineBreak;
  136. FNSHelper := TNSSupport.Create;
  137. FScratch := TFPList.Create;
  138. end;
  139. destructor TXMLWriter.Destroy;
  140. begin
  141. FScratch.Free;
  142. FNSHelper.Free;
  143. if FBufPos > FBuffer then
  144. write(FBuffer^, FBufPos-FBuffer);
  145. FreeMem(FBuffer);
  146. inherited Destroy;
  147. end;
  148. procedure TXMLWriter.wrtChars(Src: PWideChar; Length: Integer);
  149. var
  150. pb: PChar;
  151. wc: Cardinal;
  152. SrcEnd: PWideChar;
  153. begin
  154. pb := FBufPos;
  155. SrcEnd := Src + Length;
  156. while Src < SrcEnd do
  157. begin
  158. if pb >= @FBuffer[FCapacity] then
  159. begin
  160. write(FBuffer^, FCapacity);
  161. Dec(pb, FCapacity);
  162. if pb > FBuffer then
  163. Move(FBuffer[FCapacity], FBuffer^, pb - FBuffer);
  164. end;
  165. wc := Cardinal(Src^); Inc(Src);
  166. case wc of
  167. $0A: pb := StrECopy(pb, PChar(FLineBreak));
  168. $0D: begin
  169. pb := StrECopy(pb, PChar(FLineBreak));
  170. if (Src < SrcEnd) and (Src^ = #$0A) then
  171. Inc(Src);
  172. end;
  173. 0..$09, $0B, $0C, $0E..$7F: begin
  174. pb^ := char(wc); Inc(pb);
  175. end;
  176. $80..$7FF: begin
  177. pb^ := Char($C0 or (wc shr 6));
  178. pb[1] := Char($80 or (wc and $3F));
  179. Inc(pb,2);
  180. end;
  181. $D800..$DBFF: begin
  182. if (Src < SrcEnd) and (Src^ >= #$DC00) and (Src^ <= #$DFFF) then
  183. begin
  184. wc := ((LongInt(wc) - $D7C0) shl 10) + LongInt(word(Src^) xor $DC00);
  185. Inc(Src);
  186. pb^ := Char($F0 or (wc shr 18));
  187. pb[1] := Char($80 or ((wc shr 12) and $3F));
  188. pb[2] := Char($80 or ((wc shr 6) and $3F));
  189. pb[3] := Char($80 or (wc and $3F));
  190. Inc(pb,4);
  191. end
  192. else
  193. raise EConvertError.Create('High surrogate without low one');
  194. end;
  195. $DC00..$DFFF:
  196. raise EConvertError.Create('Low surrogate without high one');
  197. else // $800 >= wc > $FFFF, excluding surrogates
  198. begin
  199. pb^ := Char($E0 or (wc shr 12));
  200. pb[1] := Char($80 or ((wc shr 6) and $3F));
  201. pb[2] := Char($80 or (wc and $3F));
  202. Inc(pb,3);
  203. end;
  204. end;
  205. end;
  206. FBufPos := pb;
  207. end;
  208. procedure TXMLWriter.wrtStr(const ws: WideString); { inline }
  209. begin
  210. wrtChars(PWideChar(ws), Length(ws));
  211. end;
  212. { No checks here - buffer always has 32 extra bytes }
  213. procedure TXMLWriter.wrtChr(c: WideChar); { inline }
  214. begin
  215. FBufPos^ := char(ord(c));
  216. Inc(FBufPos);
  217. end;
  218. procedure TXMLWriter.wrtIndent; { inline }
  219. begin
  220. wrtChars(PWideChar(FIndent), FIndentCount*2+1);
  221. end;
  222. procedure TXMLWriter.IncIndent;
  223. var
  224. I, NewLen, OldLen: Integer;
  225. begin
  226. Inc(FIndentCount);
  227. if Length(FIndent) < 2 * FIndentCount then
  228. begin
  229. OldLen := Length(FIndent);
  230. NewLen := 4 * FIndentCount;
  231. SetLength(FIndent, NewLen);
  232. for I := OldLen to NewLen do
  233. FIndent[I] := ' ';
  234. end;
  235. end;
  236. procedure TXMLWriter.DecIndent; { inline }
  237. begin
  238. if FIndentCount>0 then dec(FIndentCount);
  239. end;
  240. procedure TXMLWriter.wrtQuotedLiteral(const ws: WideString);
  241. var
  242. Quote: WideChar;
  243. begin
  244. // TODO: need to check if the string also contains single quote
  245. // both quotes present is a error
  246. if Pos('"', ws) > 0 then
  247. Quote := ''''
  248. else
  249. Quote := '"';
  250. wrtChr(Quote);
  251. wrtStr(ws);
  252. wrtChr(Quote);
  253. end;
  254. const
  255. AttrSpecialChars = ['<', '"', '&', #9, #10, #13];
  256. TextSpecialChars = ['<', '>', '&'];
  257. procedure TXMLWriter.ConvWrite(const s: WideString; const SpecialChars: TSetOfChar;
  258. const SpecialCharCallback: TSpecialCharCallback);
  259. var
  260. StartPos, EndPos: Integer;
  261. begin
  262. StartPos := 1;
  263. EndPos := 1;
  264. while EndPos <= Length(s) do
  265. begin
  266. if (s[EndPos] < #255) and (Char(ord(s[EndPos])) in SpecialChars) then
  267. begin
  268. wrtChars(@s[StartPos], EndPos - StartPos);
  269. SpecialCharCallback(s[EndPos]);
  270. StartPos := EndPos + 1;
  271. end;
  272. Inc(EndPos);
  273. end;
  274. if StartPos <= length(s) then
  275. wrtChars(@s[StartPos], EndPos - StartPos);
  276. end;
  277. const
  278. QuotStr = '&quot;';
  279. AmpStr = '&amp;';
  280. ltStr = '&lt;';
  281. gtStr = '&gt;';
  282. procedure TXMLWriter.AttrSpecialCharCallback(c: WideChar);
  283. begin
  284. case c of
  285. '"': wrtStr(QuotStr);
  286. '&': wrtStr(AmpStr);
  287. '<': wrtStr(ltStr);
  288. // Escape whitespace using CharRefs to be consistent with W3 spec § 3.3.3
  289. #9: wrtStr('&#x9;');
  290. #10: wrtStr('&#xA;');
  291. #13: wrtStr('&#xD;');
  292. else
  293. wrtChr(c);
  294. end;
  295. end;
  296. procedure TXMLWriter.TextnodeSpecialCharCallback(c: WideChar);
  297. begin
  298. case c of
  299. '<': wrtStr(ltStr);
  300. '>': wrtStr(gtStr); // Required only in ']]>' literal, otherwise optional
  301. '&': wrtStr(AmpStr);
  302. else
  303. wrtChr(c);
  304. end;
  305. end;
  306. procedure TXMLWriter.WriteNode(node: TDOMNode);
  307. begin
  308. case node.NodeType of
  309. ELEMENT_NODE: VisitElement(node);
  310. ATTRIBUTE_NODE: VisitAttribute(node);
  311. TEXT_NODE: VisitText(node);
  312. CDATA_SECTION_NODE: VisitCDATA(node);
  313. ENTITY_REFERENCE_NODE: VisitEntityRef(node);
  314. PROCESSING_INSTRUCTION_NODE: VisitPI(node);
  315. COMMENT_NODE: VisitComment(node);
  316. DOCUMENT_NODE: VisitDocument(node);
  317. DOCUMENT_TYPE_NODE: VisitDocumentType(node);
  318. ENTITY_NODE,
  319. DOCUMENT_FRAGMENT_NODE: VisitFragment(node);
  320. end;
  321. end;
  322. procedure TXMLWriter.WriteNSDef(B: TBinding);
  323. begin
  324. wrtChars(' xmlns', 6);
  325. if B.Prefix^.Key <> '' then
  326. begin
  327. wrtChr(':');
  328. wrtStr(B.Prefix^.Key);
  329. end;
  330. wrtChars('="', 2);
  331. ConvWrite(B.uri, AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback);
  332. wrtChr('"');
  333. end;
  334. procedure TXMLWriter.NamespaceFixup(Element: TDOMElement);
  335. var
  336. B: TBinding;
  337. i: Integer;
  338. attr: TDOMNode;
  339. s: DOMString;
  340. action: TAttributeAction;
  341. begin
  342. FScratch.Count := 0;
  343. if Element.hasAttributes then
  344. begin
  345. for i := 0 to Element.Attributes.Length-1 do
  346. begin
  347. attr := Element.Attributes[i];
  348. if nfLevel2 in attr.Flags then
  349. begin
  350. if TDOMNode_NS(attr).NSI.NSIndex = 2 then
  351. begin
  352. if TDOMNode_NS(attr).NSI.PrefixLen = 0 then
  353. s := ''
  354. else
  355. s := attr.localName;
  356. FNSHelper.DefineBinding(s, attr.nodeValue, B);
  357. if Assigned(B) then // drop redundant namespace declarations
  358. VisitAttribute(attr);
  359. end
  360. else
  361. FScratch.Add(attr);
  362. end
  363. else if TDOMAttr(attr).Specified then // Level 1 attribute
  364. VisitAttribute(attr);
  365. end;
  366. end;
  367. FNSHelper.DefineBinding(Element.Prefix, Element.namespaceURI, B);
  368. if Assigned(B) then
  369. WriteNSDef(B);
  370. for i := 0 to FScratch.Count-1 do
  371. begin
  372. attr := TDOMNode(FScratch[i]);
  373. action := FNSHelper.CheckAttribute(attr.Prefix, attr.namespaceURI, B);
  374. if action = aaBoth then
  375. WriteNSDef(B);
  376. if action in [aaPrefix, aaBoth] then
  377. begin
  378. // use prefix from the binding, it might have been changed
  379. wrtChr(' ');
  380. wrtStr(B.Prefix^.Key);
  381. wrtChr(':');
  382. wrtStr(attr.localName);
  383. wrtChars('="', 2);
  384. // TODO: not correct w.r.t. entities
  385. ConvWrite(attr.nodeValue, AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback);
  386. wrtChr('"');
  387. end
  388. else // action = aaUnchanged, output unmodified
  389. VisitAttribute(attr);
  390. end;
  391. end;
  392. procedure TXMLWriter.VisitElement(node: TDOMNode);
  393. var
  394. i: Integer;
  395. child: TDOMNode;
  396. SavedInsideTextNode: Boolean;
  397. begin
  398. if not FInsideTextNode then
  399. wrtIndent;
  400. FNSHelper.StartElement;
  401. wrtChr('<');
  402. wrtStr(TDOMElement(node).TagName);
  403. if nfLevel2 in node.Flags then
  404. NamespaceFixup(TDOMElement(node))
  405. else if node.HasAttributes then
  406. for i := 0 to node.Attributes.Length - 1 do
  407. begin
  408. child := node.Attributes.Item[i];
  409. if TDOMAttr(child).Specified then
  410. VisitAttribute(child);
  411. end;
  412. Child := node.FirstChild;
  413. if Child = nil then
  414. wrtChars('/>', 2)
  415. else
  416. begin
  417. SavedInsideTextNode := FInsideTextNode;
  418. wrtChr('>');
  419. FInsideTextNode := Child.NodeType in [TEXT_NODE, CDATA_SECTION_NODE];
  420. IncIndent;
  421. repeat
  422. WriteNode(Child);
  423. Child := Child.NextSibling;
  424. until Child = nil;
  425. DecIndent;
  426. if not (node.LastChild.NodeType in [TEXT_NODE, CDATA_SECTION_NODE]) then
  427. wrtIndent;
  428. FInsideTextNode := SavedInsideTextNode;
  429. wrtChars('</', 2);
  430. wrtStr(TDOMElement(Node).TagName);
  431. wrtChr('>');
  432. end;
  433. FNSHelper.EndElement;
  434. end;
  435. procedure TXMLWriter.VisitText(node: TDOMNode);
  436. begin
  437. ConvWrite(TDOMCharacterData(node).Data, TextSpecialChars, {$IFDEF FPC}@{$ENDIF}TextnodeSpecialCharCallback);
  438. end;
  439. procedure TXMLWriter.VisitCDATA(node: TDOMNode);
  440. begin
  441. if not FInsideTextNode then
  442. wrtIndent;
  443. wrtChars('<![CDATA[', 9);
  444. wrtStr(TDOMCharacterData(node).Data);
  445. wrtChars(']]>', 3);
  446. end;
  447. procedure TXMLWriter.VisitEntityRef(node: TDOMNode);
  448. begin
  449. wrtChr('&');
  450. wrtStr(node.NodeName);
  451. wrtChr(';');
  452. end;
  453. procedure TXMLWriter.VisitPI(node: TDOMNode);
  454. begin
  455. if not FInsideTextNode then wrtIndent;
  456. wrtStr('<?');
  457. wrtStr(TDOMProcessingInstruction(node).Target);
  458. wrtChr(' ');
  459. wrtStr(TDOMProcessingInstruction(node).Data);
  460. wrtStr('?>');
  461. end;
  462. procedure TXMLWriter.VisitComment(node: TDOMNode);
  463. begin
  464. if not FInsideTextNode then wrtIndent;
  465. wrtChars('<!--', 4);
  466. wrtStr(TDOMCharacterData(node).Data);
  467. wrtChars('-->', 3);
  468. end;
  469. procedure TXMLWriter.VisitDocument(node: TDOMNode);
  470. var
  471. child: TDOMNode;
  472. begin
  473. wrtStr('<?xml version="');
  474. // Definitely should not escape anything here
  475. if Length(TXMLDocument(node).XMLVersion) > 0 then
  476. wrtStr(TXMLDocument(node).XMLVersion)
  477. else
  478. wrtStr('1.0');
  479. wrtChr('"');
  480. // DISABLED - we are only able write in UTF-8 which does not require labeling
  481. // writing incorrect encoding will render xml unreadable...
  482. (*
  483. if Length(TXMLDocument(node).Encoding) > 0 then
  484. begin
  485. wrtStr(' encoding="');
  486. wrtStr(TXMLDocument(node).Encoding);
  487. wrtChr('"');
  488. end;
  489. *)
  490. wrtStr('?>');
  491. // TODO: now handled as a regular PI, remove this?
  492. if Length(TXMLDocument(node).StylesheetType) > 0 then
  493. begin
  494. wrtStr(#10'<?xml-stylesheet type="');
  495. wrtStr(TXMLDocument(node).StylesheetType);
  496. wrtStr('" href="');
  497. wrtStr(TXMLDocument(node).StylesheetHRef);
  498. wrtStr('"?>');
  499. end;
  500. child := node.FirstChild;
  501. while Assigned(Child) do
  502. begin
  503. WriteNode(Child);
  504. Child := Child.NextSibling;
  505. end;
  506. wrtChars(#10, 1);
  507. end;
  508. procedure TXMLWriter.VisitAttribute(Node: TDOMNode);
  509. var
  510. Child: TDOMNode;
  511. begin
  512. wrtChr(' ');
  513. wrtStr(TDOMAttr(Node).Name);
  514. wrtChars('="', 2);
  515. Child := Node.FirstChild;
  516. while Assigned(Child) do
  517. begin
  518. case Child.NodeType of
  519. ENTITY_REFERENCE_NODE:
  520. VisitEntityRef(Child);
  521. TEXT_NODE:
  522. ConvWrite(TDOMCharacterData(Child).Data, AttrSpecialChars, {$IFDEF FPC}@{$ENDIF}AttrSpecialCharCallback);
  523. end;
  524. Child := Child.NextSibling;
  525. end;
  526. wrtChr('"');
  527. end;
  528. procedure TXMLWriter.VisitDocumentType(Node: TDOMNode);
  529. begin
  530. wrtStr(#10'<!DOCTYPE ');
  531. wrtStr(Node.NodeName);
  532. wrtChr(' ');
  533. with TDOMDocumentType(Node) do
  534. begin
  535. if PublicID <> '' then
  536. begin
  537. wrtStr('PUBLIC ');
  538. wrtQuotedLiteral(PublicID);
  539. wrtChr(' ');
  540. wrtQuotedLiteral(SystemID);
  541. end
  542. else if SystemID <> '' then
  543. begin
  544. wrtStr('SYSTEM ');
  545. wrtQuotedLiteral(SystemID);
  546. end;
  547. if InternalSubset <> '' then
  548. begin
  549. wrtChr('[');
  550. wrtStr(InternalSubset);
  551. wrtChr(']');
  552. end;
  553. end;
  554. wrtChr('>');
  555. end;
  556. procedure TXMLWriter.VisitFragment(Node: TDOMNode);
  557. var
  558. Child: TDOMNode;
  559. begin
  560. // TODO: TextDecl is probably needed
  561. // Fragment itself should not be written, only its children should...
  562. Child := Node.FirstChild;
  563. while Assigned(Child) do
  564. begin
  565. WriteNode(Child);
  566. Child := Child.NextSibling;
  567. end;
  568. end;
  569. // -------------------------------------------------------------------
  570. // Interface implementation
  571. // -------------------------------------------------------------------
  572. procedure WriteXMLFile(doc: TXMLDocument; const AFileName: String);
  573. var
  574. fs: TFileStream;
  575. begin
  576. fs := TFileStream.Create(AFileName, fmCreate);
  577. try
  578. WriteXMLFile(doc, fs);
  579. finally
  580. fs.Free;
  581. end;
  582. end;
  583. procedure WriteXMLFile(doc: TXMLDocument; var AFile: Text);
  584. begin
  585. with TTextXMLWriter.Create(AFile) do
  586. try
  587. WriteNode(doc);
  588. finally
  589. Free;
  590. end;
  591. end;
  592. procedure WriteXMLFile(doc: TXMLDocument; AStream: TStream);
  593. begin
  594. with TStreamXMLWriter.Create(AStream) do
  595. try
  596. WriteNode(doc);
  597. finally
  598. Free;
  599. end;
  600. end;
  601. procedure WriteXML(Element: TDOMNode; const AFileName: String);
  602. begin
  603. WriteXMLFile(TXMLDocument(Element), AFileName);
  604. end;
  605. procedure WriteXML(Element: TDOMNode; var AFile: Text);
  606. begin
  607. WriteXMLFile(TXMLDocument(Element), AFile);
  608. end;
  609. procedure WriteXML(Element: TDOMNode; AStream: TStream);
  610. begin
  611. WriteXMLFile(TXMLDocument(Element), AStream);
  612. end;
  613. end.