Does this kind of string slicing comparison work? jfiles / json

So, I’m trying to save an in-memory table to JSON and read it back later into the in-memory table. Using jfiles2 ver 2.28. The save is fine, but I have yet to see a load work. I either get a blank record in the memory table or an unexpected comma error.

So, I’m following along in the debugger to see what is happening and noticed this odd-looking comparison in JSONClass.LoadString (which likely has nothing to do with my problem.)

if pToParse[c] = '{{' OR pToParse[c] = '['
      Started = true
     blah blah blah

Since the string-slicing comparison as written only looks at one character, the comparison to the two curly brackets will never be true. Correct?

–David

Curly left brackets are “special” and have to be doubled up so they’re not misinterpreted by the compiler as something else such as the repeated string macro " x{20}" = 20 x’s. But it’s still just one character when processing.

3 Likes

Thanks. I saw the comparison fire true, so I knew it was working but forgot about the special characters that need to be doubled.

I usually drop those special characters out to a static-valued variable so my simple mind doesn’t think I’m trying to compare one character against two.

Looks like my loading json into a Clarion table issue may be related to having two capesoft json objects in the procedure. Instead of a generic json object, I used one for processing external data from a website and another for handling the disk save/load. I noticed that I didn’t see the second json object under the Capesoft objects and decided that wasn’t normal even though the code referring to both objects compiled OK. I could have switched to a single json object, but moved the disk save/load junk to a separate procedure, and everything immediately started working as expected.

I don’t think having 2 JSONClass objects in the procedure was the problem.
I have used many of them in the same procedure and even same class method without any issues.
There must have been something else going on for the 2 to interfere with each other.

Yeah, having multiple objects is common and they don’t interact with each other. In fact using multiple objects is usually easier.

Since you didn’t post your code that was failing, it’s impossible to do more than speculate on your result.

My suspicion would be that you did not call the load correctly. It should read:

` json.Start()

` json.setTagCase(jf:caseAsIs)
Then some for of json.Load

Im guessing you did not set the case, and it was set in your call to Save.

1 Like

Hey Bruce,

ABC templates, and I am not using MDI for a change.

Observed behavior was blank records were added to some of the IMM tables. I would get an unexpected comma error message for some tables. I believe these were coming from json that had more than a trivial amount of data in them.

This exact same code worked fine when I moved the JSON_DiskSave object to a separate procedure and called that instead of executing the statements in the calling procedure.

I believe the saving part was fine.

I set up the dSaveFolder var and then the following code runs to save a record structure ‘group’ and several in-memory tables. (The SetRemovePrefix was added while looking for my issue. I still see prefixes in the generated json and did not see any change in behavior. The upper case setting is possibly redundant since it produced uppercase before adding it. I do not have ‘names’ set, so it’s using the Clarion labels.)

Here’s the saving code:

   JSON_DiskSave.SetTagCase(jf:CaseUpper)
   JSON_DiskSave.SetRemovePrefix(true)

   JSON_DiskSave.Start()
   JSON_DiskSave.Save(PracticeList:RECORD,CLIP(dSaveFolder) & 'PracticeList_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_Provider_Setup,CLIP(dSaveFolder) & 'BCO_Provider_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_Place_Setup,CLIP(dSaveFolder) & 'BCO_Place_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_Item_Setup,CLIP(dSaveFolder) & 'BCO_Item_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_Security_Setup,CLIP(dSaveFolder) & 'BCO_Security_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_Diagnosis_Setup,CLIP(dSaveFolder) & 'BCO_Diagnosis_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_ItemModifier_Setup,CLIP(dSaveFolder) & 'BCO_ItemModifier_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_InsPlan_Setup,CLIP(dSaveFolder) & 'BCO_InsPlan_Setup.json')
   JSON_DiskSave.Start()
   JSON_DiskSave.Save(BCO_InsCompany_Setup,CLIP(dSaveFolder) & 'BCO_InsCompany_Setup.json')

Here’s the loading code:

   JSON_DiskSave.SetTagCase(jf:CaseUpper)
   JSON_DiskSave.SetRemovePrefix(true)
    
   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(PracticeList:RECORD,CLIP(dSaveFolder) & 'PracticeList_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'PracticeList_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' PracticeList_Setup records',STR:Contomed)
   
   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_Provider_Setup,CLIP(dSaveFolder) & 'BCO_Provider_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_Provider_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_Provider_Setup records',STR:Contomed)
   
   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_Place_Setup,CLIP(dSaveFolder) & 'BCO_Place_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_Place_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_Place_Setup records',STR:Contomed)
   
   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_Item_Setup,CLIP(dSaveFolder) & 'BCO_Item_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_Item_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_Item_Setup records',STR:Contomed)
   
   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_ItemModifier_Setup,CLIP(dSaveFolder) & 'BCO_ItemModifier_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_ItemModifier_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_ItemModifier_Setup records',STR:Contomed)   
   
   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_Diagnosis_Setup,CLIP(dSaveFolder) & 'BCO_Diagnosis_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_Diagnosis_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_Diagnosis_Setup records',STR:Contomed)   

   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_Security_Setup,CLIP(dSaveFolder) & 'BCO_Security_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_Security_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_Security_Setup records',STR:Contomed)
   
   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_InsPlan_Setup,CLIP(dSaveFolder) & 'BCO_InsPlan_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_InsPlan_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_InsPlan_Setup records',STR:Contomed)

   JSON_DiskSave.Start()
   IF JSON_DiskSave.Load(BCO_InsCompany_Setup,CLIP(dSaveFolder) & 'BCO_InsCompany_Setup.json') = jf:ERROR
      MESSAGE(JSON_DiskSave.GetError(),'BCO_InsCompany_Setup',ICON:Hand)
   END
   MESSAGE(JSON_DiskSave.GetRecordsInserted() & ' BCO_InsCompany_Setup records',STR:Contomed)

Did you see this post, posted recently?
IP Driver and with IN-Memory Driver Caching - ClarionHub

Be warned that the in-memory driver, despite what the documentation says, is not THREAD safe.

Can you get the code working without the In memory driver first?

Hi David

I think setting those TagCase and RemovePrefix properties would achieve nothing as whatever you set them to would be overridden in .start() that you do after this.

I don’t think that is related to your problem though as I think you are setting them to the same default values that .start() sets them to anyway.

Hi Doug,

you’re not showing your clarion structures, or an example of the JSON so it’s impossible to comment further than saying that your use of Start (as Geoff pointed out) is misunderstood.

I recommend pasting your json code into capesoft.com\jfilescode and seeing what it generates for you.

Cheers
Bruce

1 Like

Bruce,

Well, you’re seeing the code after my messing with things over a couple weeks trying isolate what was going wrong.

My understanding of .Start was to fully reset the object and I originally only had a single .Start before all the saves/loads, but in testing added it before each attempt. I did not see any behavior difference between having a single start or several.

The only “difference” between working and not working was moving the code from a routine into its own procedure. Today I’ll start backing out all the junk I added for testing.

The IMM tables are copies of TPS files. Here’s one that was ending up with a blank record. (The /threadedcontent may not be needed since I’m single-threaded/non-mdi.)

BCO_Provider_Setup   FILE,DRIVER('MEMORY','/THREADEDCONTENT'),NAME('BCOPROVS.IMM'),PRE(BCO_Provider_S),BINDABLE,CREATE,THREAD !Temp IMM used during clinic creation
PK_Provider              KEY(BCO_Provider_S:SysID),NOCASE,OPT,PRIMARY !                    
KeyProvider_OfficeID     KEY(BCO_Provider_S:OfficeID),NOCASE,OPT !                    
KeyProvider_Name         KEY(BCO_Provider_S:LastName,BCO_Provider_S:FirstName,BCO_Provider_S:MiddleInitial),DUP,NOCASE,OPT !by Last Name        
KeyProvider_Initials     KEY(BCO_Provider_S:Initials),NOCASE,OPT !                    
KeyProvider_NPI          KEY(BCO_Provider_S:NPI),DUP,NOCASE !                    
KeyProvider_RenderingSysID KEY(BCO_Provider_S:ProviderRenderingSysID),DUP,NOCASE !                    
Personal_Notes              MEMO(4096)                     !                    
Notes                       MEMO(4096)                     !                    
Record                   RECORD,PRE()
SysID                       LONG                           !Internal System Record Identifier
OfficeID                    STRING(6)                      !Office ID           
DefaultFeeScheduleSysID     LONG                           !Internal System Link to Fee Schedule
Inactive                    BYTE                           !Inactive            
Type                        STRING(1)                      !                    
License                     STRING(15)                     !License             
PostTitle                   STRING(10)                     !Post Title          
FirstName                   STRING(20)                     !First Name          
MiddleInitial               STRING(1)                      !Middle Initial      
LastName                    STRING(30)                     !Last Name           
DefaultPracticeSysID        LONG                           !                    
DefaultScheduleSysID        LONG                           !                    
ProviderRenderingSysID      LONG                           !                    
SendRenderingWithClaims     BYTE                           !                    
UseSSNorEIN                 STRING(3)                      !OBSOLETE            
SSN                         STRING(11)                     !OBSOLETE            
EIN                         STRING(11)                     !OBSOLETE            
NPI                         DECIMAL(10)                    !                    
SpecialtyCode               SHORT                          !Specialty Code      
SpecialtyLicenseNumber      STRING(15)                     !                    
EClaimIdentifier            STRING(15)                     !not used            
EClaimSiteCode              STRING(4)                      !not used            
DefaultPlaceSysID           LONG                           !                    
Initials                    STRING(5)                      !                    
ChargeEntry                 BYTE                           !                    
Referral                    BYTE                           !                    
Appointments                BYTE                           !                    
EMR                         BYTE                           !                    
UseForSurgeryAssistOnly     BYTE                           !                    
UserSysID                   LONG                           !link to security user sysid - Future Use
ChargeEntryOverrideReferrerSysID LONG                      !                    
TaxonomyCode                STRING(10)                     !                    
DEANumber                   STRING(20)                     !                    
Personal_AddressZipCode     STRING(10)                     !Address Zip Code    
Personal_AddressCountry     STRING(2)                      !                    
Personal_AddressState       STRING(3)                      !Address State       
Personal_AddressCity        STRING(40)                     !Address City        
Personal_AddressLine1       STRING(80)                     !Address Line 1      
Personal_AddressLine2       STRING(80)                     !Address Line 2      
Personal_HomeTelephone      DECIMAL(10)                    !Home Telephone      
Personal_HomeTelephoneNote  STRING(40)                     !Home Telephone Note 
Personal_WorkTelephone      DECIMAL(10)                    !Work Telephone      
Personal_WorkTelephoneNote  STRING(40)                     !                    
Personal_CellTelephone      DECIMAL(10)                    !Cell Telephone      
Personal_CellTelephoneNote  STRING(40)                     !Cell Telephone Note 
Personal_OtherTelephone     DECIMAL(10)                    !Other Telephone     
Personal_OtherTelephoneNote STRING(40)                     !Other Extension/Note
Personal_FaxNumber          DECIMAL(10)                    !Fax Number          
Personal_FaxNumberNote      STRING(40)                     !Notes               
Personal_EmailAddress       STRING(80)                     !                    
                         END
                     END

Here’s the json that is produced now in a working state. (I do not believe it to be any different from before but will double-check that.)

[
	{
		"BCO_PROVIDER_S:SYSID" : 1,
		"BCO_PROVIDER_S:OFFICEID" : "1",
		"BCO_PROVIDER_S:DEFAULTFEESCHEDULESYSID" : 0,
		"BCO_PROVIDER_S:INACTIVE" : 0,
		"BCO_PROVIDER_S:TYPE" : "D",
		"BCO_PROVIDER_S:LICENSE" : "LICNUMHERE",
		"BCO_PROVIDER_S:POSTTITLE" : "POSTTITLE",
		"BCO_PROVIDER_S:FIRSTNAME" : "DAVID",
		"BCO_PROVIDER_S:MIDDLEINITIAL" : "",
		"BCO_PROVIDER_S:LASTNAME" : "JUNG",
		"BCO_PROVIDER_S:DEFAULTPRACTICESYSID" : 0,
		"BCO_PROVIDER_S:DEFAULTSCHEDULESYSID" : 0,
		"BCO_PROVIDER_S:PROVIDERRENDERINGSYSID" : 0,
		"BCO_PROVIDER_S:SENDRENDERINGWITHCLAIMS" : 0,
		"BCO_PROVIDER_S:USESSNOREIN" : "",
		"BCO_PROVIDER_S:SSN" : "",
		"BCO_PROVIDER_S:EIN" : "",
		"BCO_PROVIDER_S:NPI" : 1234567890,
		"BCO_PROVIDER_S:SPECIALTYCODE" : 0,
		"BCO_PROVIDER_S:SPECIALTYLICENSENUMBER" : "",
		"BCO_PROVIDER_S:ECLAIMIDENTIFIER" : "",
		"BCO_PROVIDER_S:ECLAIMSITECODE" : "",
		"BCO_PROVIDER_S:DEFAULTPLACESYSID" : 0,
		"BCO_PROVIDER_S:INITIALS" : "",
		"BCO_PROVIDER_S:CHARGEENTRY" : 1,
		"BCO_PROVIDER_S:REFERRAL" : 0,
		"BCO_PROVIDER_S:APPOINTMENTS" : 0,
		"BCO_PROVIDER_S:EMR" : 0,
		"BCO_PROVIDER_S:USEFORSURGERYASSISTONLY" : 0,
		"BCO_PROVIDER_S:USERSYSID" : 0,
		"BCO_PROVIDER_S:CHARGEENTRYOVERRIDEREFERRERSYSID" : 0,
		"BCO_PROVIDER_S:TAXONOMYCODE" : "TAXCDHERE",
		"BCO_PROVIDER_S:DEANUMBER" : "",
		"BCO_PROVIDER_S:PERSONAL_ADDRESSZIPCODE" : "",
		"BCO_PROVIDER_S:PERSONAL_ADDRESSCOUNTRY" : "",
		"BCO_PROVIDER_S:PERSONAL_ADDRESSSTATE" : "",
		"BCO_PROVIDER_S:PERSONAL_ADDRESSCITY" : "",
		"BCO_PROVIDER_S:PERSONAL_ADDRESSLINE1" : "",
		"BCO_PROVIDER_S:PERSONAL_ADDRESSLINE2" : "",
		"BCO_PROVIDER_S:PERSONAL_HOMETELEPHONE" : 0,
		"BCO_PROVIDER_S:PERSONAL_HOMETELEPHONENOTE" : "",
		"BCO_PROVIDER_S:PERSONAL_WORKTELEPHONE" : 0,
		"BCO_PROVIDER_S:PERSONAL_WORKTELEPHONENOTE" : "",
		"BCO_PROVIDER_S:PERSONAL_CELLTELEPHONE" : 0,
		"BCO_PROVIDER_S:PERSONAL_CELLTELEPHONENOTE" : "",
		"BCO_PROVIDER_S:PERSONAL_OTHERTELEPHONE" : 0,
		"BCO_PROVIDER_S:PERSONAL_OTHERTELEPHONENOTE" : "",
		"BCO_PROVIDER_S:PERSONAL_FAXNUMBER" : 0,
		"BCO_PROVIDER_S:PERSONAL_FAXNUMBERNOTE" : "",
		"BCO_PROVIDER_S:PERSONAL_EMAILADDRESS" : "",
		"BCO_PROVIDER_S:PERSONAL_NOTES" : "",
		"BCO_PROVIDER_S:NOTES" : ""
	}
]

Putting the json into jfilescodes gives me a queue, has the labels as name(), all the numeric types are reasl, and the two memos become relatively short strings. I’m not using the memos in this situation, so those don’t matter. I don’t see any surprises.

The documentation explicitly says that .Start does not reset SetTagCase, SetRemovePrefix, and a few other properties.

I assumed I didn’t need the case thing since it was already generating json in uppercase.

RemovePrefix didn’t seem to do anything since I ended up with the prefix in the json either way.

The original code didn’t have these statements and adding them did not cause a behavior change. I’ll be removing them today to see if I can break it again while the code is in a separate procedure.

I’m not using the IMM as a cache for an existing physical table but more as a smart memory queue that the templates treat like a table for browses.

And I’m not multi-threaded/mdi.

–David

I’m in Clarion 9.1 if that matters.

Removed all the extraneous messages and starts attempting to see what was happening and still working fine.

Moved the code out of the procedures and back into routines. Added back the object.

Still works OK, so I guess this is resolved, although I sort of wanted to know how I was breaking it.

Clarion sometimes makes me wonder about my career choices over the years.

Something I’ve noticed working on the templates recently which might account for what you have seen, is changes made to a template dont always update so I have to remove the template and reselect the template in order to see the new changes.
I dont have the dct and template registry set to read only either, and I do have the Re Register when changed option ticked and Update Template Chain when edited also ticked.

I think the template registry has its own sort of version control in the template registry. I saw this with template screen controls, sometimes they would update and sometimes they wouldn’t.

This might be something you are seeing which is causing you problems?

That might be true in c6, its not true in c11.

In c11 template changes always apply (assuming you have the switch on.)

I hope so because I have found #Validate code that doest get triggered when I click generate all, which is a bit frustrating as I write these templates out, but it only needs to work upon input anyway, so this behaviour may not change in C11, its something I’ll have watch out for and test.

That would tend to indicate a scope problem of some sort for some variables somewhere.
i.e. you’re ending up not using the variable you thought you were

I think that documentation must be wrong or perhaps outdated. The best up-to-date documentation is the source code. The first few lines of JSONPropertyClass.Start are:

  Clear(self.TableQueue)
  self.RemovePrefix = true
  self.MaxPrefixLengthInJSON = 5
  self.PrefixChars = ':'
  self.ReplaceColons = true
  self.FreeQueueBeforeLoad = true
  self.UpdateFileOnLoad = true
  self.ReplacementChars = '_'
  self.TagCase = jF:CaseUpper
1 Like