sqldbrestxml.pp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2019 by the Free Pascal development team
  4. SQLDB REST bridge : XML input/output
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit sqldbrestxml;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. {$mode objfpc}{$H+}
  15. interface
  16. {$IFDEF FPC_DOTTEDUNITS}
  17. uses
  18. System.Classes, System.SysUtils, System.DateUtils, Data.Db, FpJson.Data, Xml.Dom, Xml.Read, Xml.Writer, FpWeb.RestBridge.Schema,FpWeb.RestBridge.IO, FpWeb.RestBridge.Bridge;
  19. {$ELSE FPC_DOTTEDUNITS}
  20. uses
  21. Classes, SysUtils, DateUtils, db,fpjson, dom, XMLRead, XMLWrite,sqldbrestschema,sqldbrestio, sqldbrestbridge;
  22. {$ENDIF FPC_DOTTEDUNITS}
  23. Type
  24. { TXMLInputStreamer }
  25. TXMLInputStreamer = Class(TRestInputStreamer)
  26. private
  27. FXML: TXMLDocument;
  28. FPacket : TDOMElement;
  29. FData : TDOMElement;
  30. FRow : TDOMElement;
  31. Protected
  32. function GetNodeText(N: TDOmNode): UnicodeString;
  33. Public
  34. Destructor Destroy; override;
  35. Class Function GetContentType: String; override;
  36. Function SelectObject(aIndex : Integer) : Boolean; override;
  37. function GetContentField(aName: UTF8string): TJSONData; override;
  38. procedure InitStreaming; override;
  39. Property XML : TXMLDocument Read FXML;
  40. Property Packet : TDOMElement Read FPacket;
  41. Property Data : TDOMElement Read FData;
  42. Property Row : TDOMElement Read FRow;
  43. end;
  44. { TXMLOutputStreamer }
  45. TXMLOutputStreamer = Class(TRestOutputStreamer)
  46. Private
  47. FXML: TXMLDocument;
  48. FData : TDOMElement;
  49. FRow: TDOMElement;
  50. FRoot: TDomElement;
  51. Public
  52. procedure EndData; override;
  53. procedure EndRow; override;
  54. procedure FinalizeOutput; override;
  55. procedure StartData; override;
  56. procedure StartRow; override;
  57. // Return Nil for null field.
  58. function FieldToXML(aPair: TRestFieldPair): TDOMElement; virtual;
  59. procedure WriteField(aPair: TRestFieldPair); override;
  60. procedure WriteMetadata(aFieldList: TRestFieldPairArray); override;
  61. Procedure CreateErrorContent(aCode : Integer; Const aMessage: String); override;
  62. Property XML : TXMLDocument Read FXML;
  63. Property Data : TDOMelement Read FData;
  64. Property Row : TDOMelement Read FRow;
  65. Public
  66. Destructor Destroy; override;
  67. Class Function GetContentType: String; override;
  68. Class Function FileExtension: String; override;
  69. procedure InitStreaming; override;
  70. end;
  71. implementation
  72. {$IFDEF FPC_DOTTEDUNITS}
  73. uses FpWeb.RestBridge.Consts;
  74. {$ELSE FPC_DOTTEDUNITS}
  75. uses sqldbrestconst;
  76. {$ENDIF FPC_DOTTEDUNITS}
  77. { TXMLInputStreamer }
  78. destructor TXMLInputStreamer.Destroy;
  79. begin
  80. FreeAndNil(FXML);
  81. inherited Destroy;
  82. end;
  83. class function TXMLInputStreamer.GetContentType: String;
  84. begin
  85. Result:='text/xml';
  86. end;
  87. Class Function TXMLOutputStreamer.FileExtension: String;
  88. begin
  89. Result:='.xml';
  90. end;
  91. function TXMLInputStreamer.SelectObject(aIndex: Integer): Boolean;
  92. Var
  93. N : TDomNode;
  94. NN : UnicodeString;
  95. begin
  96. Result:=False;
  97. NN:=UTF8Decode(GetString(rpRowName));
  98. N:=FData.FindNode(NN);
  99. While (aIndex>0) and (N<>Nil) and (N.NodeName<>NN) and (N.NodeType<>ELEMENT_NODE) do
  100. begin
  101. N:=N.NextSibling;
  102. Dec(aIndex);
  103. end;
  104. Result:=(aIndex=0) and (N<>Nil);
  105. If Result then
  106. FRow:=N as TDomElement
  107. else
  108. FRow:=Nil;
  109. end;
  110. Function TXMLInputStreamer.GetNodeText(N : TDOmNode) : UnicodeString;
  111. Var
  112. V : TDomNode;
  113. begin
  114. Result:='';
  115. V:=N.FirstChild;
  116. While (V<>Nil) and (V.NodeType<>TEXT_NODE) do
  117. V:=V.NextSibling;
  118. If Assigned(V) then
  119. Result:=V.NodeValue;
  120. end;
  121. function TXMLInputStreamer.GetContentField(aName: UTF8string): TJSONData;
  122. Var
  123. NN : UnicodeString;
  124. N : TDomNode;
  125. begin
  126. NN:=UTF8Decode(aName);
  127. N:=FRow.FindNode(NN);
  128. if Assigned(N) and (N.NodeType=ELEMENT_NODE) then
  129. Result:=TJSONString.Create(UTF8Encode(GetNodeText(N)));
  130. end;
  131. procedure TXMLInputStreamer.InitStreaming;
  132. Var
  133. Msg : String;
  134. N : TDomNode;
  135. NN : UnicodeString;
  136. begin
  137. FreeAndNil(FXML);
  138. if Stream.Size<=0 then
  139. exit;
  140. try
  141. ReadXMLFile(FXML,Stream);
  142. except
  143. On E : Exception do
  144. begin
  145. Msg:=E.Message;
  146. FXML:=Nil;
  147. end;
  148. end;
  149. if (FXML=Nil) then
  150. Raise ESQLDBRest.CreateFmt(Statuses.GetStatusCode(rsInvalidContent),SErrInvalidXMLInput,[Msg]);
  151. FPacket:=FXML.DocumentElement;
  152. NN:=UTF8Decode(GetString(rpXMLDocumentRoot));
  153. if (NN<>'') then
  154. begin
  155. if FPacket.NodeName<>NN then
  156. Raise ESQLDBRest.CreateFmt(Statuses.GetStatusCode(rsInvalidContent),SErrInvalidXMLInput,[SErrMissingDocumentRoot]);
  157. NN:=UTF8Decode(GetString(rpDataRoot));
  158. N:=FPacket.FindNode(NN);
  159. end
  160. else
  161. begin
  162. // if Documentroot is empty, data packet is the root element
  163. NN:=UTF8Decode(GetString(rpDataRoot));
  164. if (Packet.NodeName=NN) then
  165. N:=FPacket
  166. else
  167. N:=Nil
  168. end;
  169. if Not (Assigned(N) and (N is TDOMelement)) then
  170. Raise ESQLDBRest.CreateFmt(Statuses.GetStatusCode(rsInvalidContent),SErrInvalidXMLInputMissingElement,[NN]);
  171. FData:=(N as TDOMelement);
  172. end;
  173. { TXMLOutputStreamer }
  174. procedure TXMLOutputStreamer.EndData;
  175. begin
  176. FData:=Nil;
  177. end;
  178. procedure TXMLOutputStreamer.EndRow;
  179. begin
  180. FRow:=Nil;
  181. end;
  182. procedure TXMLOutputStreamer.FinalizeOutput;
  183. begin
  184. {$IFNDEF VER3_0}
  185. if Not (ooHumanReadable in OutputOptions) then
  186. begin
  187. With TDOMWriter.Create(Stream,FXML) do
  188. try
  189. LineBreak:='';
  190. IndentSize:=0;
  191. WriteNode(FXML);
  192. finally
  193. Free;
  194. end;
  195. end
  196. else
  197. {$ENDIF}
  198. WriteXML(FXML,Stream);
  199. FreeAndNil(FXML);
  200. end;
  201. procedure TXMLOutputStreamer.StartData;
  202. begin
  203. FData:=FXML.CreateElement(UTF8Decode(GetString(rpDataRoot)));
  204. FRoot.AppendChild(FData);
  205. end;
  206. procedure TXMLOutputStreamer.StartRow;
  207. begin
  208. if (FRow<>Nil) then
  209. Raise ESQLDBRest.Create(Statuses.GetStatusCode(rsError),SErrDoubleRowStart);
  210. FRow:=FXML.CreateElement(UTF8Decode(GetString(rpRowName)));
  211. FData.AppendChild(FRow);
  212. end;
  213. Function TXMLOutputStreamer.FieldToXML(aPair: TRestFieldPair) : TDomElement;
  214. Var
  215. F : TField;
  216. S : UTF8String;
  217. begin
  218. Result:=Nil;
  219. F:=aPair.DBField;;
  220. If (aPair.RestField.FieldType=rftUnknown) then
  221. raise ESQLDBRest.CreateFmt(Statuses.GetStatusCode(rsError),SErrUnsupportedRestFieldType, [aPair.RestField.PublicName]);
  222. If (F.IsNull) then
  223. Exit;
  224. S:=FieldToString(aPair.RestField.FieldType,F);
  225. Result:=FXML.CreateElement(UTF8Decode(aPair.RestField.PublicName));
  226. Result.AppendChild(FXML.CreateTextNode(UTF8Decode(S)));
  227. end;
  228. procedure TXMLOutputStreamer.WriteField(aPair: TRestFieldPair);
  229. Var
  230. D : TDOMElement;
  231. N : UTF8String;
  232. begin
  233. N:=aPair.RestField.PublicName;
  234. if FRow=Nil then
  235. Raise ESQLDBRest.CreateFmt(Statuses.GetStatusCode(rsError),SErrFieldWithoutRow,[N]);
  236. D:=FieldToXML(aPair);
  237. if (D=Nil) and (not HasOption(ooSparse)) then
  238. D:=FXML.CreateElement(UTF8Decode(aPair.RestField.PublicName));
  239. if D<>Nil then
  240. FRow.AppendChild(D);
  241. end;
  242. procedure TXMLOutputStreamer.WriteMetadata(aFieldList: TRestFieldPairArray);
  243. Var
  244. M : TDOMElement;
  245. F : TDomElement;
  246. P : TREstFieldPair;
  247. begin
  248. F:=FXML.CreateElement(UTF8Decode(GetString(rpMetaDataFields)));
  249. M:=FXML.CreateElement(UTF8Decode(GetString(rpMetaDataRoot)));
  250. M.AppendChild(F);
  251. FRoot.AppendChild(M);
  252. M:=F;
  253. For P in aFieldList do
  254. begin
  255. F:=FXML.CreateElement(UTF8Decode(GetString(rpMetaDataField)));
  256. M.AppendChild(F);
  257. F[UTF8Decode(GetString(rpFieldNameProp))]:=UTF8Decode(P.RestField.PublicName);
  258. F[UTF8Decode(GetString(rpFieldTypeProp))]:=UTF8Decode(typenames[P.RestField.FieldType]);
  259. Case P.RestField.FieldType of
  260. rftDate : F[UTF8Decode(GetString(rpFieldDateFormatProp))]:=UTF8Decode(GetString(rpDateFormat));
  261. rftTime : F[UTF8Decode(GetString(rpFieldDateFormatProp))]:=UTF8Decode(GetString(rpTimeFormat));
  262. rftDateTime : F[UTF8Decode(GetString(rpFieldDateFormatProp))]:=UTF8Decode(GetString(rpDateTimeFormat));
  263. rftString : F[UTF8Decode(GetString(rpFieldMaxLenProp))]:=UTF8Decode(IntToStr(P.DBField.Size));
  264. else
  265. ;
  266. end;
  267. end;
  268. end;
  269. class function TXMLOutputStreamer.GetContentType: String;
  270. begin
  271. Result:='text/xml';
  272. end;
  273. procedure TXMLOutputStreamer.CreateErrorContent(aCode: Integer; const aMessage: String);
  274. Var
  275. ErrorObj : TDomElement;
  276. begin
  277. ErrorObj:=FXML.CreateElement(UTF8Decode(GetString(rpErrorRoot)));
  278. ErrorObj['code']:=UTF8Decode(IntToStr(aCode));
  279. ErrorObj['message']:=UTF8Decode(aMessage);
  280. FRoot.AppendChild(ErrorObj);
  281. end;
  282. destructor TXMLOutputStreamer.Destroy;
  283. begin
  284. FreeAndNil(FXML);
  285. inherited Destroy;
  286. end;
  287. procedure TXMLOutputStreamer.InitStreaming;
  288. begin
  289. FXML:=TXMLDocument.Create;
  290. FRoot:=FXML.CreateElement('datapacket');
  291. FXML.AppendChild(FRoot);
  292. end;
  293. Initialization
  294. TXMLInputStreamer.RegisterStreamer('xml');
  295. TXMLOutputStreamer.RegisterStreamer('xml');
  296. end.