![]() | ![]() Products ![]() ![]() ![]() ![]() |
Home » Technical Support » Elevate Web Builder Technical Support » Support Forums » Elevate Web Builder General » View Thread |
Messages 11 to 20 of 24 total |
![]() |
Tue, Apr 21 2015 12:52 PM | Permanent Link |
Tim Young [Elevate Software] Elevate Software, Inc. ![]() | Mark,
<< Is there anything in EWB2, perhaps based on that capabilities you've outlined, that can populate the properties for a standard class from some JSON? >> Yes, what I just posted. ![]() published, make sure that your class descends from TPersistent, and then fire up a TReader instance to load the JSON into the instance. MyReader:=TReader.Create(MyJSONString); Load(MyReader); (The TPersistent.Load method is currently protected, so you'll have to do this from within the class instance itself, but that's because there will be a more user-friendly LoadFromString() method that does all of the TReader handling for you). I'm probably going to hook this into the TServerRequest functionality to automate it even further at some point. Tim Young Elevate Software www.elevatesoft.com |
Thu, Apr 23 2015 6:48 AM | Permanent Link |
Mark Brooks Slikware | "Tim Young [Elevate Software]" wrote:
>>Yes, what I just posted. ![]() >>published, make sure that your class descends from TPersistent, and then >>fire up a TReader instance to load the JSON into the instance. Ok. So this continues to excite me. A lot. And not just for me, but for what this will enable new EWB users to achieve AND converts! I have had a brief attempt at using this capability with our REST API. My new test classes (see below) read standard properties perfectly, however I cannot seem to handle embedded array properties. Can you shed any light please? // ---------------------------------------------------------------- // Base Castrum Class to Simplify De-Serialisation // ---------------------------------------------------------------- type TCastrumBase = class(TPersistent) public procedure LoadFromJSON(const AJSONString: string); end; implementation procedure TCastrumBase.LoadFromJSON(const AJSONString: string); var R: TReader; begin R := TReader.Create(AJSONString); try Self.Load(R); finally R.Free; end; end; // --------------------------------------------------------------------------------------------- // Castrum Login Class To Hold Details Received From A Login Request // --------------------------------------------------------------------------------------------- type TCastrumLogin = class(TCastrumBase) private fUserId: integer; fUserName: string; fName: string; fAvatarLink: string; fPreviousLoginDate: string; fSysAdmin: boolean; fUserAdmin: boolean; fWorkspaceAdmin: boolean; published property UserId: integer read fUserId write fUserId; property UserName: string read fUserName write fUserName; property Name: string read fName write fName; property AvatarLink: string read fAvatarLink write fAvatarLink; end; // ------------------------------------------------------------------------------------------- // Castrum User Class To Hold Details Received From A User Request // ------------------------------------------------------------------------------------------- type TCastrumUser = class(TCastrumBase) private fName: string; published property Name: string read fName write fName; end; // ----------------------------------------------------------------------------------------- // Castrum Response Class To Hold Details From A Generic Request // ----------------------------------------------------------------------------------------- type TCastrumResponse = class(TCastrumBase) private fLogin: TCastrumLogin; fUsers: array of TCastrumUser; fAPIVersion: string; fMessage: string; fServerDate: string; fSuccess: boolean; fToken: string; public constructor Create; override; destructor Destroy; override; published property Login: TCastrumLogin read fLogin; property Users: array of TCastrumUser read fUsers write fUsers; property APIVersion: string read fAPIVersion write fAPIVersion; property Message: string read fMessage write fMessage; property ServerDate: string read fServerDate write fServerDate; property Success: boolean read fSuccess write fSuccess; property Token: string read fToken write fToken; end; implementation constructor TCastrumResponse.Create; begin inherited Create; fLogin := TCastrumLogin.Create; end; destructor TCastrumResponse.Destroy; begin fLogin.Free; inherited Destroy; end; The Castrum Response class is used for all Castrum API calls. In the example shown, it caters for Login and Users calls. The remaining properties are common across all calls. The Login call populates the Login and generic properties correctly. The Users call does not populate the array property correctly. I imagine that my issue is something to do with the way that I've defined the array in order for the internal TReader to process the JSON correctly. Cheers Mark |
Thu, Apr 23 2015 7:22 AM | Permanent Link |
Matthew Jones | Mark Brooks wrote:
> Ok. So this continues to excite me. A lot. And not just for me, but > for what this will enable new EWB users to achieve AND converts! Hmm, it does raise interesting opportunities. My thought was that first I'd do is make an application that has those objects, fill them in in the Pascal code, and then do the "get JSON" call to see what form it takes. Then you'd be able to see if you can alter the JSON text and feed it back in. I would presume that to be the first step in understanding how arrays are handled (assuming they are at the moment). If they aren't, then it would be a useful exercise to help Tim to edit the framework to make it. I did this with the existing parser, making it fit more exotic situations like I managed to need. Tim didn't apply directly IIRC, but the proof of concept and examples presumably helped. Knowing Tim though, he has either already done it, or knows how to do it better - EWB is very well thought through. -- Matthew Jones |
Thu, Apr 23 2015 3:26 PM | Permanent Link |
Tim Young [Elevate Software] Elevate Software, Inc. ![]() | Mark,
<< I have had a brief attempt at using this capability with our REST API. My new test classes (see below) read standard properties perfectly, however I cannot seem to handle embedded array properties. Can you shed any light please? >> What you need to do is override the TPersistent.LoadProperty method to handle array properties. You can see how this is done with the TComponent class: function TComponent.LoadProperty(AReader: TReader): Boolean; var TempPropertyName: String; TempClass: TClass; TempComponent: TComponent; TempName: String; begin Result:=False; TempPropertyName:=AReader.GetPropertyName; if (TempPropertyName <> '') then begin if SameText(TempPropertyName,PERSISTENT_LOAD_CLASSNAME) then begin Result:=True; AReader.SkipPropertyName; AReader.SkipPropertySeparator; TempClass:=ClassByName(AReader.ReadString); if Assigned(TempClass) then begin TempComponent:=TComponentClass(TempClass).Create(TComponent(AReader.RootComponent)); TempComponent.LoadProperties(AReader); end; end else if SameText(TempPropertyName,PERSISTENT_LOAD_NAME) then begin Result:=True; AReader.SkipPropertyName; AReader.SkipPropertySeparator; TempName:=AReader.ReadString; SetProperty(TempPropertyName,TempName); AReader.RootComponent.SetInstance(TempName,Self); end else if SameText(TempPropertyName,PERSISTENT_LOAD_PROPERTIES) then begin Result:=True; AReader.SkipPropertyName; AReader.SkipPropertySeparator; LoadObject(AReader); end else if SameText(TempPropertyName,PERSISTENT_LOAD_COMPONENTS) then begin Result:=True; AReader.SkipPropertyName; AReader.SkipPropertySeparator; LoadArray(AReader); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Here !!! end else Result:=inherited LoadProperty(AReader); end; end; Essentially, array properties must be handled via code, at least for now. I can't remember exactly what the issue was with having EWB automatically load them, but it probably has something to do with not being able to get the correct RTTI for such constructs. Tim Young Elevate Software www.elevatesoft.com |
Mon, Apr 27 2015 4:49 AM | Permanent Link |
Mark Brooks Slikware | "Tim Young [Elevate Software]" wrote:
>>What you need to do is override the TPersistent.LoadProperty method to >>handle array properties. You can see how this is done with the TComponent >>class: No worries Tim. Thought this was potentially too good to be true. Nonetheless, still a significant advance. Take care and good luck with the May release. |
Mon, Apr 27 2015 5:44 PM | Permanent Link |
Tim Young [Elevate Software] Elevate Software, Inc. ![]() | Mark,
<< Thought this was potentially too good to be true. >> I certainly wouldn't characterize it that way. It does exactly what you want with almost zero code, and you don't need to do anything other than what the code that I showed you is doing: function TCastrumResponse.LoadProperty(AReader: TReader): Boolean; var TempPropertyName: String; begin Result:=False; TempPropertyName:=AReader.GetPropertyName; if (TempPropertyName <> '') then begin if SameText(TempPropertyName,'Users') then begin Result:=True; AReader.SkipPropertyName; AReader.SkipPropertySeparator; LoadArray(AReader); end else Result:=inherited LoadProperty(AReader); end; end; I also remembered the reason for the requirement of code for the arrays: it's the bounds of the "list" that needs to be managed by the containing object. IOW, when it comes to things like lists, they can come in all sorts of different forms. Your example just happens to choose the most basic example, which I'll most likely automate at some point. Many times, lists embedded in other components/controls can be string lists, object lists, etc., and only the containing object knows the most efficient way to manage setting the bounds of the list and adding items to the list. << Nonetheless, still a significant advance. Take care and good luck with the May release. >> Thanks. ![]() Tim Young Elevate Software www.elevatesoft.com |
Tue, Apr 28 2015 4:07 PM | Permanent Link |
Mark Brooks Slikware | "Tim Young [Elevate Software]" wrote:
>>I certainly wouldn't characterize it that way. It does exactly what you >>want with almost zero code, and you don't need to do anything other than >>what the code that I showed you is doing: Point taken Tim. Guess I just want the moon on a stick! I will play further. Thanks |
Wed, Apr 29 2015 6:41 AM | Permanent Link |
Tim Young [Elevate Software] Elevate Software, Inc. ![]() | Mark,
Of course, as soon as I tell you that, I realize that what I said isn't entirely correct. You'll also need to override this TPersistent method: function LoadArrayElement(AReader: TReader): Boolean; virtual; in your classes that have array properties. It can just look like this: function TCastrumResponse.LoadArrayElement(AReader: TReader): Boolean; begin // Here you need to put the code for expanding the array, as necessary Result:=inherited LoadArrayElement(AReader: TReader); end; The ancestor TPersistent.LoadArrayElement method already knows how to load any TPersistent descendant classes, so that part is all set and it will be able to take it from there in terms of loading each TCastrumUser instance. Tim Young Elevate Software www.elevatesoft.com |
Fri, May 1 2015 6:22 AM | Permanent Link |
Mark Brooks Slikware | "Tim Young [Elevate Software]" wrote:
>>function TCastrumResponse.LoadArrayElement(AReader: TReader): Boolean; >>begin >> // Here you need to put the code for expanding the array, as necessary >> Result:=inherited LoadArrayElement(AReader: TReader); >>end; >>The ancestor TPersistent.LoadArrayElement method already knows how to load >>any TPersistent descendant classes, so that part is all set and it will be >>able to take it from there in terms of loading each TCastrumUser instance. Sorry Tim. I must be having a bad week, but I am just not getting this? I don't understand the parsing process that takes place when the TPersistent descendent parses array properties and therefore don't understand where to "intercept" and how to allocate appropriate storage at that point. Embarrassed somewhat. |
Fri, May 1 2015 7:52 AM | Permanent Link |
Tim Young [Elevate Software] Elevate Software, Inc. ![]() | Mark,
<< Sorry Tim. I must be having a bad week, but I am just not getting this? I don't understand the parsing process that takes place when the TPersistent descendent parses array properties and therefore don't understand where to "intercept" and how to allocate appropriate storage at that point. Embarrassed somewhat. >> Actually, I left out a little bit still (hard keeping tabs on things this week...), so it's not surprising that it's hard to understand. It works like this: 1) You start to load your TCastrumResponse class using the TPersistent.Load method. 2) This starts the process of EWB looping through your published properties, loading each one using the LoadProperty method. 3) Since you overrode the LoadProperty method for the TCastrumResponse class, it will get called for each property. 4) Once the LoadProperty method is called for the Users property, your LoadProperty method will *not* call the inherited LoadProperty, but rather will call the LoadArray method. This will start parsing of the array of TCastrumUser instances. 5) Because you overrode the LoadArrayElement method for the TCastrumResponse class, it will get called for each array element. 6) In the LoadArrayElement method, you need to expand the TCastrumUser array by one, create a new TCastrumUser instance, and then load it: function TCastrumResponse.LoadArrayElement(AReader: TReader): Boolean; var TempLength: Integer; begin TempLength:=Length(FUsers); Inc(TempLength); SetLength(FUsers,TempLength); FUsers[TempLength]:=TCastrumUser.Create; FUsers[TempLength].LoadObject(AReader); end; This last bit is the part that I left out. Previously I had it calling the inherited LoadArrayElement, which isn't correct. If you need to load more than one type of array for a given class, then you'll simply want to save the property name to a class variable in the LoadProperty method, and then use that property name here to determine which property array you're loading: function TCastrumResponse.LoadProperty(AReader: TReader): Boolean; begin Result:=False; FPropertyName:=AReader.GetPropertyName; // FPropertyName: String is the class variable to add if (FPropertyName <> '') then begin if SameText(FPropertyName,'Users') or SameText(FPropertyName,'SomeOtherArray') then begin Result:=True; AReader.SkipPropertyName; AReader.SkipPropertySeparator; LoadArray(AReader); end else Result:=inherited LoadProperty(AReader); end; end; function TCastrumResponse.LoadArrayElement(AReader: TReader): Boolean; var TempLength: Integer; begin if SameText(FPropertyName,'Users') then begin TempLength:=Length(FUsers); Inc(TempLength); SetLength(FUsers,TempLength); FUsers[TempLength]:=TCastrumUser.Create; FUsers[TempLength].LoadObject(AReader); end else if SameText(FPropertyName,'SomeOtherArray') then ....... end; Tim Young Elevate Software www.elevatesoft.com |
« Previous Page | Page 2 of 3 | Next Page » |
Jump to Page: 1 2 3 |
This web page was last updated on Friday, March 28, 2025 at 03:46 AM | Privacy Policy![]() © 2025 Elevate Software, Inc. All Rights Reserved Questions or comments ? ![]() |