sqldbrestxml.pp 8.4 KB

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