Welcome! Log In Create A New Profile

Advanced

WD26/WD27: HRecordToJSON / HJSONToRecord SOLVED!

Posted by gpredl 
WD26/WD27: HRecordToJSON / HJSONToRecord SOLVED!
May 10, 2022 07:33AM
Hi,

has anybody ever successfully used both functions HRecordToJSON and HJSONToRecord ??? Whatever I do, the HRecordToJSON works just fine (= no error, doesn't mean a lot) but HJSONToRecord ALWAYS gives back an error telling me "Error caused by 2 parameter, Invalid JSON format."

Is there any secret to these functions? Or is it just a plain bug?

Kind regards,
Guenter Predl
office@windev.at



Edited 2 time(s). Last edit at 05/14/2022 07:48AM by gpredl.
Did you try on a file WITHOUT binary memos?

the help says : Composite key, binary memo or binary items are ignored.

But it doesn't says what that does to the json and if it can be re-imported
Re: WD26/WD27: HRecordToJSON / HJSONToRecord
May 12, 2022 08:15AM
Hi Argus,

yes, I sent a demo project to PC Soft Tech Support containing a single file with just 4 items, none of them is of text/binary etc memo type. In my real project I listed all of the items (175!) to export / import without any memo items.

Demo project: [www.windev.at]

Kind regards,
Guenter Predl
office@windev.at
Re: WD26/WD27: HRecordToJSON / HJSONToRecord SOLVED!
May 14, 2022 08:15AM
Hi,

obviously I'm that simple-minded to think that reading a record by HRecordToJSON() and storing the result in a file and then reading the file and restoring the record by using HJSONToRecord() would restore the original record. This doesn't work at all! The string structure output by HRecordToJSON() is incompatible with a subsequent HJSONToRecord() !!

However, the solution is much simpler: don't use HJSONToRecord( ..) at all !!

// The name of the HFSQL data file is PERSON
// saving a record of PERSON to a data file is simple
XString = HRecordToJSON(PERSON,"PERSON_NAME,PERSON_PROFESSION,PERSON_AGE")
// note: I did not save PERSON_NUM, the number of the person just because I want to re-use the data but not the number. However, the PERSON_NUM is now used for identification of the record:
fSaveText(CompleteDir(fExeDir)+NumToString(PERSON_NUM,"04d")+"_PERSON.JSON",XString)
FILL_COMBO()

// Reading the file and putting its parts into a bunch of edit controls:

MyJSON is JSON
XString is ANSI string

// Get string from file, which has been selected in a ComboBox !
XString = fLoadText(CompleteDir(fExeDir)+NoSpace(COMBO.DisplayedValue))

// convert the string to JSON
MyJSON = StringToJSON(XString)

// For example, set the Edit-controls of a form now!
PERSON_NAME = MyJSON.PERSON.PERSON_NAME
PERSON_PROFESSION = MyJSON.PERSON.PERSON_PROFESSION
PERSON_AGE = MyJSON.PERSON.PERSON_AGE

// Here we are!

Same game is possible using indirection if the data file has many more items ... I tested what happens to those JSON characters like \u00e4 .. surprise, it reconverts them properly to strings!!

Kind regards,
Guenter Predl
office@windev.at



Edited 1 time(s). Last edit at 05/14/2022 08:23AM by gpredl.
Re: WD26/WD27: HRecordToJSON / HJSONToRecord SOLVED!
May 16, 2022 10:01AM
For those who are interested:

Herewith our Serialise and Deserialise methods from the wxRecord class.
It contains a Query Record variable.

1/ Serialise:
FUNCTION ObjSerialise(_MsgLst is wxMessageList, _IncludeTextMemos is boolean = True, _IncludeBinaryMemos is boolean = True)sad smileyboolean,JSON)
//Local variables
Msg is wxMessage
SerBuf is Buffer
ResJSON is JSON

// Clear all error messages in the list previously set by this function
_MsgLst:ClearListForCaller(dbgInfo(dbgElement,dbgCurrentProcess))

//Do logic
WHEN EXCEPTION IN 
	// We first serialise the object without the data sources, since natively WX cannot serialise binary fields in a data source
	Serialize(object,SerBuf,psdJSON)
	IF NOT ErrorOccurred THEN
		//Assign the JSON
		ResJSON = SerBuf
	
		//Now add the data source into the json
		FieldList is string = HListItem(QRYRec, hLstDetail)
		
		//First we add regular fields (non-binary fields)
		FOR EACH STRING FieldDef OF FieldList SEPARATED BY CR
			FieldName	is string	= ExtractString(FieldDef,1,TAcool smiley
			FieldType	is string	= ExtractString(FieldDef,3,TAcool smiley
			//Do we have a binary element in the QrySelect member
			IF NOT FieldType IN (hItemBinary, hItemBinaryMemo, hItemBinaryMemo4, hItemPicture, hItemTextMemo, hItemUnicodeMemo ) THEN
				//Serialise a Regular Field
				{"ResJSON.QryRec."+FieldName} = {"QRYRec."+FieldName}
			END
		END
		
		//Last we add binary fields
		IF _IncludeTextMemos _OR_ _IncludeBinaryMemos THEN
			FOR EACH STRING FieldDef OF FieldList SEPARATED BY CR
				FieldName	is string	= ExtractString(FieldDef,1,TAcool smiley
				FieldType	is string	= ExtractString(FieldDef,3,TAcool smiley
				//Do we have a binary element in the QrySelect member
				IF FieldType IN (hItemBinary, hItemBinaryMemo, hItemBinaryMemo4, hItemPicture, hItemTextMemo, hItemUnicodeMemo ) THEN
					//Serialise the binary field
					SerBinB64 is string
					SerBinB64 = Encode({":QRYRec."+FieldName},encodeBASE64NoCR)
					{"ResJSON.QryRec.###BinaryData###."+FieldName+".Content"} = SerBinB64
					IF FieldType IN (hItemTextMemo, hItemUnicodeMemo) AND _IncludeTextMemos THEN
						{"ResJSON.QryRec.###BinaryData###."+FieldName+".WXType"}="TextMemo"					
					ELSE
						IF _IncludeBinaryMemos THEN
							IF FieldType = hItemPicture THEN
								{"ResJSON.QryRec.###BinaryData###."+FieldName+".WXType"}="ImageMemo"					
							ELSE
								{"ResJSON.QryRec.###BinaryData###."+FieldName+".WXType"}="BinaryMemo"					
							END
						END
						<COMPILE IF ConfigurationType<>Android>
							//Add also Binary Memo Metadata
							BinInfo is string = HInfoMemo({":QryRec."+FieldName})
							{"ResJSON.QryRec.###BinaryData###."+FieldName+".Type"}=ExtractString(BinInfo,1,TAcool smiley
							{"ResJSON.QryRec.###BinaryData###."+FieldName+".Name"}=ExtractString(BinInfo,2,TAcool smiley
							{"ResJSON.QryRec.###BinaryData###."+FieldName+".Size"}=ExtractString(BinInfo,3,TAcool smiley
							{"ResJSON.QryRec.###BinaryData###."+FieldName+".Date"}=ExtractString(BinInfo,4,TAcool smiley
							{"ResJSON.QryRec.###BinaryData###."+FieldName+".Time"}=ExtractString(BinInfo,5,TAcool smiley
							{"ResJSON.QryRec.###BinaryData###."+FieldName+".Info"}=ExtractString(BinInfo,6,TAcool smiley
						<END>
					END
				END
			END
		END
	
		//Add Some Meta Data
		ResJSON.Metadata.SerialiseVersion	= SerialiseVersion
		ResJSON.Metadata.DataSource			= object..Class
		ResJSON.Metadata.PrimaryTable		= DBRec..AssociatedFile
	
		RESULT (True, ResJSON)
	ELSE
		Msg.AddToList(_MsgLst,wxMessage.MSG_SYSERROR,ErrorInfo(errCode),"An error occurred while serialising the object.",ErrorInfo(errMessage))
		RESULT (False, ResJSON)
	END
DO
	Msg.AddToList(_MsgLst,wxMessage.MSG_SYSEXCEPTION,ExceptionInfo(errCode),"An exception was encountered.",ExceptionInfo(errMessage))
	wxApplication.LogException(_MsgLst)
	<COMPILE IF ConfigurationType<>Android>
		ExceptionEnable()		
	<END>
	RESULT (False, ResJSON)
END

2/ Deserialise (back to a class instance including the Query Record)
FUNCTION ObjDeserialise(_MsgLst is wxMessageList, _JSON is JSON):boolean
//Local variables
Msg		is wxMessage
SerBuf	is Buffer <utile>

// Clear all error messages in the list previously set by this function
_MsgLst:ClearListForCaller(dbgInfo(dbgElement,dbgCurrentProcess))

//Do logic
WHEN EXCEPTION IN 
	//Allow only compatible deserialisations
	Trace(_JSON.ToString)
	IF _JSON.Metadata.DataSource ~= object..class 
		
		//Now Deserialise all basic members
		<COMPILE IF ConfigurationType=Android>
			FOR EACH Mbr OF _JSON
				IF NOT Lower(Mbr..Name) IN ("qryrec","metadata") THEN
					{":"+Mbr..Name} = Mbr..Value
				END
			END			
		<ELSE>
			//Parse all base type members from the JSON
			ObjDef is Definition = GetDefinition(object)	
			FOR EACH Mbr OF ObjDef.Variable
				IF Mbr.Definition <> Null THEN
					IF Mbr.Definition.Type IN (wlInt, wlInt_1, wlInt_2, wlInt_4, wlInt_8, wlUnsignedInt_1, wlUnsignedInt_2, wlUnsignedInt_4, wlUnsignedInt_8, ...
						wlReal, wlReal_4, wlReal_8, wlNumeric, wlCurrency, ... 
					    wlUUID, wlUUID_128, wlUUID_256, wlString, wlAnsiString, wlFixedString, wlASCIIZString, wlCharacter, ...
					    wlDate, wlDateTime, wlTime, wlDuration, ...
					    wlBoolean, wlEmpty)
						IF {"_JSON."+Mbr..Name}..Exist THEN
							{":"+Mbr..Name} = {"_JSON."+Mbr..Name}..Value
						END
					END
				END
			END
		<END>		
		
		//Now deserialise the QRYRec data from the json
		FieldList is string = HListItem(QRYRec, hLstDetail)
		FOR EACH STRING FieldDef OF FieldList SEPARATED BY CR
			FieldName	is string	= ExtractString(FieldDef,1,TAcool smiley
			FieldType	is string	= ExtractString(FieldDef,3,TAcool smiley
			//Do we have a binary element in the QrySelect member
			IF FieldType IN (hItemBinary, hItemBinaryMemo, hItemBinaryMemo4, hItemPicture, hItemTextMemo, hItemUnicodeMemo)
				//Serialise the binary field
				SerBinB64 is Buffer
				SerBinB64 = Decode({"_JSON.QryRec.###BinaryData###."+FieldName+".Content"}..Value,encodeBASE64NoCR)
				{":QRYRec."+FieldName} = SerBinB64
			ELSE
				//Deserialise Field
				{":QRYRec."+FieldName} = {"_JSON.QryRec."+FieldName}..Value
			END
		END
		//Return OK
		RESULT True
	ELSE
		Msg.AddToList(_MsgLst,wxMessage.MSG_SYSERROR,ErrorInfo(errCode),"An error occurred while deserialising into the object.",...
		"Incompatible Data Source types: "+_JSON.Metadata.DataSource+"/"+object..class+" (Info: "+_JSON.Metadata.PrimaryTable+"/"+DBRec..AssociatedFile+")")
		RESULT False
	END
DO
	Msg.AddToList(_MsgLst,wxMessage.MSG_SYSEXCEPTION,ExceptionInfo(errCode),"An exception was encountered.",ExceptionInfo(errMessage))
	wxApplication.LogException(_MsgLst)
	<COMPILE IF ConfigurationType<>Android>
		ExceptionEnable()		
	<END>
	RESULT False
END

Cheers

Peter H.
Re: WD26/WD27: HRecordToJSON / HJSONToRecord SOLVED!
May 16, 2022 10:19AM
Hi Peter,

thank you!! As I can see, your code includes binary memos + images as well. HRecordToJSON ignores any pictures.

As you could see, I chose a relatively simple way to set the Edit controls of a Form with the content of the record from a file. HJSONToRecord(..) doesn't work at all. Of course, for sending a QueryRecord by Web Service that code is by far not sufficient.

Thank you!!!

Kind regards,
Guenter Predl
office@windev.at
Author:

Your Email:


Subject:


Spam prevention:
Please, enter the code that you see below in the input field. This is for blocking bots that try to post this form automatically. If the code is hard to read, then just try to guess it right. If you enter the wrong code, a new image is created and you get another chance to enter it right.
Message: