sqldbrestxml.pp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. xmlwrite.WriteXML(FXML,Stream);
  169. FreeAndNil(FXML);
  170. end;
  171. procedure TXMLOutputStreamer.StartData;
  172. begin
  173. FData:=FXML.CreateElement(UTF8Decode(GetString(rpDataRoot)));
  174. FRoot.AppendChild(FData);
  175. end;
  176. procedure TXMLOutputStreamer.StartRow;
  177. begin
  178. if (FRow<>Nil) then
  179. Raise ESQLDBRest.Create(Statuses.GetStatusCode(rsError),SErrDoubleRowStart);
  180. FRow:=FXML.CreateElement(UTF8Decode(GetString(rpRowName)));
  181. FData.AppendChild(FRow);
  182. end;
  183. Function TXMLOutputStreamer.FieldToXML(aPair: TRestFieldPair) : TDomElement;
  184. Var
  185. F : TField;
  186. S : UTF8String;
  187. begin
  188. Result:=Nil;
  189. F:=aPair.DBField;;
  190. If (aPair.RestField.FieldType=rftUnknown) then
  191. raise ESQLDBRest.CreateFmt(Statuses.GetStatusCode(rsError),SErrUnsupportedRestFieldType, [aPair.RestField.PublicName]);
  192. If (F.IsNull) then
  193. Exit;
  194. S:=FieldToString(aPair.RestField.FieldType,F);
  195. Result:=FXML.CreateElement(UTF8Decode(aPair.RestField.PublicName));
  196. Result.AppendChild(FXML.CreateTextNode(UTF8Decode(S)));
  197. end;
  198. procedure TXMLOutputStreamer.WriteField(aPair: TRestFieldPair);
  199. Var
  200. D : TDOMElement;
  201. N : UTF8String;
  202. begin
  203. N:=aPair.RestField.PublicName;
  204. if FRow=Nil then
  205. Raise ESQLDBRest.CreateFmt(Statuses.GetStatusCode(rsError),SErrFieldWithoutRow,[N]);
  206. D:=FieldToXML(aPair);
  207. if (D=Nil) and (not HasOption(ooSparse)) then
  208. D:=FXML.CreateElement(UTF8Decode(aPair.RestField.PublicName));
  209. if D<>Nil then
  210. FRow.AppendChild(D);
  211. end;
  212. procedure TXMLOutputStreamer.WriteMetadata(aFieldList: TRestFieldPairArray);
  213. Var
  214. M : TDOMElement;
  215. F : TDomElement;
  216. P : TREstFieldPair;
  217. begin
  218. F:=FXML.CreateElement(UTF8Decode(GetString(rpMetaDataFields)));
  219. M:=FXML.CreateElement(UTF8Decode(GetString(rpMetaDataRoot)));
  220. M.AppendChild(F);
  221. FRoot.AppendChild(M);
  222. M:=F;
  223. For P in aFieldList do
  224. begin
  225. F:=FXML.CreateElement(UTF8Decode(GetString(rpMetaDataField)));
  226. M.AppendChild(F);
  227. F[UTF8Decode(GetString(rpFieldNameProp))]:=UTF8Decode(P.RestField.PublicName);
  228. F[UTF8Decode(GetString(rpFieldTypeProp))]:=UTF8Decode(typenames[P.RestField.FieldType]);
  229. Case P.RestField.FieldType of
  230. rftDate : F[UTF8Decode(GetString(rpFieldDateFormatProp))]:=UTF8Decode(GetString(rpDateFormat));
  231. rftTime : F[UTF8Decode(GetString(rpFieldDateFormatProp))]:=UTF8Decode(GetString(rpTimeFormat));
  232. rftDateTime : F[UTF8Decode(GetString(rpFieldDateFormatProp))]:=UTF8Decode(GetString(rpDateTimeFormat));
  233. rftString : F[UTF8Decode(GetString(rpFieldMaxLenProp))]:=UTF8Decode(IntToStr(P.DBField.Size));
  234. end;
  235. end;
  236. end;
  237. class function TXMLOutputStreamer.GetContentType: String;
  238. begin
  239. Result:='text/xml';
  240. end;
  241. procedure TXMLOutputStreamer.CreateErrorContent(aCode: Integer; const aMessage: String);
  242. Var
  243. ErrorObj : TDomElement;
  244. begin
  245. ErrorObj:=FXML.CreateElement(UTF8Decode(GetString(rpErrorRoot)));
  246. ErrorObj['code']:=UTF8Decode(IntToStr(aCode));
  247. ErrorObj['message']:=UTF8Decode(aMessage);
  248. FRoot.AppendChild(ErrorObj);
  249. end;
  250. destructor TXMLOutputStreamer.Destroy;
  251. begin
  252. FreeAndNil(FXML);
  253. inherited Destroy;
  254. end;
  255. procedure TXMLOutputStreamer.InitStreaming;
  256. begin
  257. FXML:=TXMLDocument.Create;
  258. FRoot:=FXML.CreateElement('datapacket');
  259. FXML.AppendChild(FRoot);
  260. end;
  261. Initialization
  262. TXMLInputStreamer.RegisterStreamer('xml');
  263. TXMLOutputStreamer.RegisterStreamer('xml');
  264. end.