jFiles is derived from code written by Dries Driessen.
      It is used here under license.
     For the purposes of this section it is assumed that
      the JSON object is declared as follows; 
      
      json JSONClass 
      
      Loading from a JSON File into a Group,
        Queue or Table
       Loading a JSON file into a structure is a single
        line of code.
        
        json.Load(Structure, JsonFileName,
            <boundary>) 
        
        You can use a Table, Queue or Group using this approach.
        
        If the JSON file contains named objects, then specify the name as the
        boundary parameter. If you omit this parameter, but the JSON you want to
        import is inside a named JSON object, then nothing will be imported.
        
        The fields in the JSON file are matched to fields in your record
        structure. There are three properties which affect this matching. For 
more information on matching see the
        section on 
Field Matching Tips.
        
        After the load is complete two methods will be available for your use;
        
        
GetRecordsInserted()
          GetRecordsUpdated()
        
        These methods return a counter of the records inserted (into the Table
        or Queue) and the number of records updated (in the Table). These
        counters are reset to zero by a call to the 
Start
        method.
        
        When the 
Load method completes it will
        return either 
jf:Ok (0)  or 
jf:ERROR
        (-1). If 
jf:Error is returned then a
        description of the error is in the Error property. A call to the 
ErrorTrap method will have been made as well.
        
        
The use of a JSON Array (ie list)
        structure usually implies that the Clarion structure should also be a
        List, and equally the use of a JSON Object (not a list) structure
        implies a group, or value on the Clarion side. However jFiles
        automatically supports simple object into a List structure (the same as
        a list with one value) and also importing a 
List
          structure into a Clarion Group structure.
        
      
Loading from a JSON String into a
        Group, Queue or Table
       Loading a JSON string into a structure is a single
        line of code. It's the same as for a table, but uses a StringTheory
        object instead of a file name.
        
        str  StringTheory
            code
            json.Load(Structure, str, <boundary>)
        
        
        This is the same as the 
Loading from a File
        method described above, except that the source is a StringTheory string,
        and not a file.
      
Loading a JSON String into the JSON
        object for processing
       The JSONobject can be loaded from a string or file
        using one of these methods; 
        
        json.LoadString(StringTheory)
        
        or 
        
        
json.LoadFile(Filename)
        
        Once the JSON text has been loaded into the object it is automatically
        parsed, and then you can directly inspect the contents of the JSON.
        
        
jsonItem  &JSONClass
          str  StringTheory
            code
            str.SetValue('json text is here')
            json.LoadString(str)
        
        Remember that all JSON files are just a collection of items. Each item
        in turn can be a collection of more items. Once the string is loaded
        into the JSON object the Records method returns the number of items at
        the top level;
        
        
x = json.Records()
        
        Like a queue you can loop through the items in the object using the Get
        method
        
        
loop x = 1 to json.records()
            jsonItem &= json.Get(x)
          end
        
        You can check the name of the item using the Name method
        
        
loop x = 1 to json.records()
            jsonItem &= json.Get(x)
            if jsonItem.Name() = 'Customers'
              ! do something
            end
          end
        
        and of course you can get the value using the GetValue method. So;
        
        
loop x = 1 to json.records()
            jsonItem &= json.Get(x)
            if jsonItem.Name() = 'Customers'
              somevalue = jsonItem.GetValue()
            end
          end
        
        an alternative to looping through the items in the object is to use the
        
GetByName method.
        
        
jsonItem &= json.GetByName('customers')
        
        You can test if you got something back by checking that 
jsonItem
          is not NULL. This is important - using a NULL object will
        result in a GPF
        
        
If not jsonItem &= Null
        
        Once you have a specific item, you can load this into a structure;
        
        
jsonItem.Load(structure)
        
        The field names in your JSON will need to match the fieldnames in your
        structure. See 
Field Matching Tips for
        hits on making the matching process better.
        
        Putting the code together it looks something like this;
        
        
jsonItem &= json.GetByName('customers')
        If not jsonItem &= Null
          jsonItem.Load(structure)
          End
        
        If you have a specific field in the JSON you can extract it using the 
GetValueByName method
        
        
somestring = json.GetValuebyName('surname')
        
      Deformatting incoming JSON Values
       By default the contents of a JSON field will be
        copied into your Clarion field during the LOAD. However the format of
        the JSON field may not be the format you wish to store in your Clarion
        field. For example an incoming date, formatted as 
yyyy/mm/dd
        may need to be deformatted in order to store it in a Clarion LONG field.
        
        
        This can be done by adding the desired Picture to the NAME attribute of
        the field. For example;
        
        
InvoiceQueue Queue
          Date Long,name('Date | @D3')
          End
        
        All 
 StringTheory Extended Pictures are supported. For
        more information on the name attribute see 
Extended
          Name Attributes.
        
        
jFiles 1
         The process above is considerably simpler than the
          one below, and should be used wherever possible. If it is not
          possible, or you are reading old code (and trying to understand it)
          then the jFiles 1 method is described below. 
          
          To do this a method is provided;
          
          json            Class(jsonClass)
            DeformatValue     Procedure (String pName, String
            pValue),STRING,VIRTUAL
                            End
          
          Please note that the name being passed in here, in the pName
            parameter, is the JSON field name, not the Clarion field
          name. Using this name as the identifier it is possible to create a
          Case statement before the parent call, deformatting the value as
          required. For example;
          
          case pName
            of 'DATUM'
              Return deformat(pValue,'@d1')
            of 'TIME'
              Return deformat(pValue,'@t4')
            end
        
      Filtering Records when Loading
       When loading into a Table or Queue it can be useful
        to filter out records which are not desired. 
        This is done by embedding code into the 
ValidateRecord
        method. 
        
        
json             Class(jsonClass)
          ValidateRecord     Procedure(),Long,Proc,Virtual
                           End
        
        If (your code in) this method returns 
 jf:filtered
        then the record is not added to the Table or Queue and the next
        record is processed. If (your code in) this method returns 
jf:outofrange
        then the import is considered complete, and no further records are
        loaded. If (your code in)this method returns 
jf:ok
        then the record is added to the table or queue.
        
        When this method is called the table record, or queue record, has been
        primed with the record that is about to be written.
        
        Example
        
        
json.ValidateRecord Procedure ()
            Code
            If inv:date < date(1,1,2018) then Return jf:filtered.
            Return jf:ok
        
      Match By Number
       While JSON is commonly formatted as "name value
        pairs", it doesn't have to be this way. It could just be a collection of
        arrays, with no names at all. For example say this is the contents of
        the a.json file;
        
        [
              [45,80],
              [30,85],
              [16,4]
          ]
        
        To load this into a structure it's clearly not possible to match the
        json to field names in the structure, rather it needs to import based on
        the position of the value. This can be done by setting the MatchByNumber
          property to true. For example;
        
        aq QUEUE
          tem string(255)
          pressure string(255)
          end
          
          code 
            json.start() 
            json.SetMatchByNumber(true)
            json.load(aq,'a.json')
      
      Loading a
        StringTheory object within a Group or Queue
       Consider this JSON structure;
        
        {
              "product":"NetTalk",
              "description":"A really long description goes here",
              "installfile":"base64encoded file install - could be very large"
          }
        
        In this situation one (or more) of the items in the group are of
        indeterminate length. This is clearly a situation for using a
        StringTheory object.The Clarion structure should look like this;
        
        
ProductGroup  Group
          Product         String(100),name('product')
          Description     &StringTheory,name('description | stringtheory')
          InstallFile     &StringTheory,name('installfile
          | stringtheory')
                        End
        
        Note the use of the 
Extended Name
          Attribute.
        You are free to create the StringTheory objects - or you can leave them
        as null (they will be automatically created). 
        However whichever approach you take the objects MUST be disposed of
        after you are done with them.
        
        
        
Example
        
        ProductGroup.Description &= New StringTheory 
          ProductGroup.InstallFile &= New StringTheory  
          
          json.Start()
          json.SetTagCase(jf:CaseAsIs)
          json.Load(ProductGroup,'somefile.json')
          
        If not ProductGroup.Description &= Null 
         
            ProductGroup.Description.Trace()  
          End
          
         
          json.DisposeGroup(ProductGroup)   NOT optional
         
        Tip: As with any pointer in a structure, it may end up being NULL if the
        incoming JSON does not include this field. So you should ALWAYS test the
        pointer before using it. For example;
        
        
      
Loading a Queue within a Group
       Consider this JSON structure;
        
        {
              "error": "validation_error",
              "cause": [
                  { "cause_id": 369,
                    "message": "some message"
                  },
                  { "cause_id": 121,
                    "message": "some other message"
                  },
              ]
          }
        
        It's clearly a group (since the outside brackets are 
{})
        but it contains a list (
cause [] ), or, in
        other words, a queue inside a group.
        
        In Clarion this group can be declared as 
        
        
CauseQueueType     Queue,Type
          cause_id             long,name('cause_id')
          message              string(255),name('message')
                             End
          
          ErrorGroup         Group
          Error                String(100) , Name('Error')
          Cause                &CauseQueueType, Name('cause | queue')
                             End
        
        Note the use of a queue type declaration inside a group declaration
        here. Note the use of the 
Extended
          Name Attribute.
        
        Before using this group, a new queue must be assigned;
        
        
  ErrorGroup.Cause &= new CauseQueueType 
        
        then load it
        
        
  json.SetTagCase(jF:CaseAny)
            json.Load(ErrorGroup, 'whatever.json')
        
        then you can access the queue as any normal queue. For example
        
        
  GET(ErrorGroup.Cause,1)
            Json.Trace(ErrorGroup.Cause.Message)
        
        After using it 
remember to dispose
        the queue
        
        
Dispose(ErrorGroup.Cause)
      Loading a Queue within a Queue
       Consider this JSON structure;
        
        [{
              "Id": 60,
              "Data": {
                  "TotalPaymentAmount": 90.63,
                  "Discounts": [{
                      "DiscountType": "DASH",
                      "Amount": 136.27
                  }, {
                      "DiscountType": "LOTS",
                      "Amount": 210.27
                  }]
              }
          }, {
              "Id": 61,
              "Data": {
                  "TotalPaymentAmount": 90.63,
                  "Discounts": [{
                      "DiscountType": "DASH",
                      "Amount": 325.27
                  }, {
                      "DiscountType": "LOTS",
                      "Amount": 499.27
                  }]
              }
          }, {
              "Id": 63,
              "Data": {
                  "TotalPaymentAmount": 90.63,
                  "Discounts": {
                      "DiscountType": "SPECIAL",
                      "Amount": 525.27
                  } 
              }
          }]
        
        This is a List, containing multiple records.  Inside each record is a
        group (Data) and inside each group is a queue of discounts.
        
        The (simplified) Clarion version of this structure would ideally look
        like this (but as you'll see this is not allowed)
        
        Messages            Queue
          Id                    Long
          Data                  Group
          TotalPaymentAmount      Decimal(10,2)
          Discounts               Queue
          Amount                    Decimal(10,2)
                                  End
                                End
                              End
        
        Clarion does however not allow queues to be declared inside groups, or
        inside other queues. what you are allowed are references to queues. This
        can be tricky to work with to make sure there are no memory leaks -
        fortunately jFiles includes methods to make this safe.
        
        The data declaration looks like this;
        
        DiscountsQueueType  Queue,Type
          DiscountType          String(20),name('DiscountType')
          Amount                Decimal(10,2),name('Amount')
                              End     
          
          Messages            Queue
          Id                    Long,name('Id')
          Data                  Group,name('Data')
          TotalPaymentAmount      Decimal(10,2),name('TotalPaymentsAmount')
          Discounts               &DiscountsQueueType,name('Discounts |
          queue')
                                End
                              End
        
        
        Note the use of the Extended Name Attribute in the declaration of the
        Discounts field.
        
        Because the DiscountsQueueType is in scope
        in your code, it's necessary for your code to NEW
        this type. This is done in the NewPointer method.
        So the declaration looks like this;
        
        json         class(jsonClass) 
          NewPointer     procedure(String pColumnName),Derived
                       end
        
        And the method itself looks like this;
        
        json.NewPointer Procedure(String pColumnName)
          code 
            Case pColumnName 
            Of 'Discounts'  ! note this is teh json tag name, not the label.
          case sensitive.
            MessagesQ.Data.DiscountsQ &= NEW DiscountsQueueType
            End
        
        that's all there is to it. The rest of the load is as normal;
        
          json.Start()
            json.SetTagCase(jf:CaseAsIs) ! CaseAsIs uses the NAME attribute, not
          the LABEL
            json.Load(MessagesQ,str) 
        
        
        Note: Although jFiles includes code to support &SomeGroupType  in
        the class, the Clarion NEW command does not support Groupp Types, so
        effectively &GROUPs in Queues or Groups are not supported.
        
jFiles 1
        An alternative approach is to use two Queues. 
        (This approach is obsolete now, but is included here to understand code
        that already exists) 
        
        MessagesQ           Queue
          Id                    Long,name('Id')
          Data                  Group,name('Data')
          TotalPaymentAmount      Decimal(10,2),name('TotalPaymentAmount')
                                End
                              End
          
          DiscountsQ          Queue  
          MessageId             Long
          DiscountType          String(20),name('DiscountType')
          Amount                Decimal(10,2),name('Amount')
                              End
        
        All the messages go in one queue, and all the discounts go in another.
        The MessageID serves to determine which discounts belong in which queue.
        
        The code to populate these two queues looks like this;
        
        json              Class(JSONClass)
          AddQueueRecord      Procedure (Long pFirst=0),Long,Proc,Virtual
                            End
          oneMess           &jsonClass
          node              &jsonClass
          MessageId         long
          
            CODE
            Free(MessagesQ) 
            Free(DiscountsQ)
          
        
            json.Start()
            json.SetFreeQueueBeforeLoad(False)
            json.SetRemovePrefix(True)
            json.SetReplaceColons(True)
            json.SetTagCase(jf:CaseAsIs)  ! 
            json.LoadString(jsonStr)
            json.Load(MessagesQ)  
          
            
            Loop x = 1 to json.records()
              oneMess &= json.Get(x)
                  node &= onemess.GetByName('Id')
              if not node &= NULL
                MessageId = node.GetValue()  
                ! now get the discounts
                node &= oneMess.GetByName('Discounts',2)  
                if not node &= NULL 
                  node.Load(DiscountsQ)        end 
              end 
            end 
          
        
          json.AddQueueRecord Procedure (Long pFirst=0)
          Q   &Queue
            CODE 
            Q &= self.Q
            If Q &= DiscountsQ 
              DiscountsQ.MessageId = MessageId  
            End 
            Parent.AddQueueRecord(pFirst)
        
        As you can see in the above approach the AddQueueRecord
          is overridden so that the extra field in the DiscountsQ
          is properly primed.
        
        Aside: You may notice that the call to load the DiscountsQ
          uses the node object ( node.Load(DiscountsQ)
        ) but the object being overridden is the  json
        object. Usually this would mean the code would not run, however jFiles
        automatically manages this, as node is a reference to a jFiles object
        inside the object called json, code in the
        json object automatically applies to the
        nodes as well.
        
      
      Importing Recursive Nodes
       Consider the following json;
        
        [{ 
              "id": 1, 
              "Parent": 0, 
              "name": "John Smith", 
              "children": [{ 
                  "id": 2, 
                  "Parent": 1, 
                  "name": "Sally Smith"
               },{
                  "id": 3, 
                  "Parent": 1, 
                  "name": "Teresa Smith"
              }]
          }]
         
        At first glance this looks like a list, but it's not. It's a collection
        of nodes, related to each other, in a tree like pattern. The parent and
        child notes however do have a common structure, which suggests this
        could be loaded into a queue.
        
        TestQ         QUEUE  
          id              LONG,NAME('id') 
          Parent          LONG,NAME('Parent')  
          name            STRING(100),NAME('name')  
                        END  
        
        To import a node structure into a list structure requires the engine to
        walk through the nodes, adding each one to the queue. This is done using
        the LoadNodes method.
        
          json.start()
            json.SetTagCase(jF:CaseAsIs)
            json.LoadFile('whatever.json') 
            json.LoadNodes(TestQ,'children')
        
        One advantage of the JSON above is that it contains a parent field,
        where every child node explicitly links to its parent. In many cases
        though this is not the case. Consider this JSON
        
        [{ 
              "id": 1, 
              "name": "John Smith", 
              "children": [{ 
                  "id": 2, 
                  "name": "Sally Smith"
               },{
                  "id": 3, 
                  "name": "Teresa Smith"
              }]
          }]
        
        Now the identity of the parent is determined by the location of the node
        in the tree. If we move this into a queue then the location is lost. 
        The above structure suggests a queue like this;
        
        TestQ         QUEUE  
          id              LONG,NAME('id') 
          name            STRING(100),NAME('name')  
                        END 
        
        Since the location is being lost an additional field to store the
        "parentId" is required.
        
        TestQ         QUEUE  
          id              LONG,NAME('id') 
          name            STRING(100),NAME('name')  
          ParentID        LONG  
                        END 
        
        
        Then in the call to LoadNodes this field, and the name of the
        identifying field, must be included.
        
          json.start()
            json.SetTagCase(jF:CaseAsIs)
            json.LoadFile('whatever.json') 
            json.LoadNodes(TestQ,'children',TestQ.ParentID,'id')
        
        The above code tells jFiles to use the ID field of one node as the
        ParentID of all child nodes.
        
      
      Pointers
       For known pointer types see;
        
        
        The following approach in this section should only be for other kinds of
        pointers.
        
        Consider a queue or group structure which contains a reference. For
        example;
        
        
SomeQueue   Queue
          Date          Long
          Time          Long
          Notes         &Something
                      End
        
        When data is moved into the 
Notes field
        then a more complicated assignment needs to take place (for each
        incoming record in the JSON).
        
        In order to make this possible code needs to be added to the 
AssignField
          and 
 GetColumnType methods.
        
        
json          Class(jsonClass)
          GetColumnType   Procedure (*Group pGroup, *Cstring
          pColumnName),Long,VIRTUAL
          AssignField     Procedure (*Group pGroup, *Cstring pColumnName,
          JSONClass pJson),VIRTUAL
                        End
        
        The first thing to do is tell the system that a complex assignment needs
        to take place for a specific field. This is done in the 
GetColumnType
          method. In this example;
        
        
json.GetColumnType Procedure (*Group pGroup,
          *Cstring pColumnName) 
            CODE
            Case pColumnName 
            of 'Notes' 
              return json:Reference
            end
        
        You are responsible for the assignment. The assignment is done as
        follows:
        
        
json.AssignField Procedure (*Group pGroup, *Cstring
          pColumnName, JSONClass pJson)
            CODE 
            Case pColumnName 
            of 'Notes' 
              SomeQueue.Notes &= NEW(Something) 
              SomeQueue.Notes = pJson.GetValue()
            End 
            Parent.AssignField(pGroup,pColumnName,pJson)
        
        As you can see in the above code, the column name is tested, and if it
        is the reference column (Notes) then a SomeThing object is created, and
        the value is placed in there.
        
        This is done for each record in the queue. This means that you need to
        be very careful when deleting records from the queue and when freeing
        the queue. And the queue MUST be correctly emptied before ending the
        procedure or a memory leak will occur.
        
        For example;
        
        
FreeSomeQueue Routine
            loop while records(q:Product)
              Get(q:Product,1)
              Dispose(SomeQueue.Notes)
              Delete(q:Product)
            End
        
      Detecting Omitted and NULL values
       When importing into a structure (Group, Queue or
        Table), each field in the structure is primed from the incoming JSON.
        This works perfectly if the JSON contains a field with the same name. 
        
        If the field does not exist in the incoming JSON then the field in the
        structure is cleared, to a blank string or a zero value.
        If the field does exist, but the value is set as null, then again the
        field is cleared or set to zero.
        
        This approach is convenient and simple, but does make it difficult when
        you are working later on with the structure (perhaps a group or queue)
        to identify fields which were explicitly set to blank as distinct from
        fields which were omitted, as distinct from fields set to null.
        
        To overcome this problem jFiles allows you to set specific values for
        omitted strings or numbers, and specific values for null strings and
        null numbers. While this doesn't necessarily solve the problem in all
        cases (you still need to have some values the user cannot use) it will
        be suitable in most cases.
        
        For example;
        
        json.Start()
          json.SetTagCase(jf:CaseAsIs)
          
          json.SetWriteOmittedString(True)
          json.SetOmittedStringDefaultValue('//omitted//')
          json.SetWriteOmittedNumeric(true)
          json.SetOmittedNumericDefaultValue(2415919000)
          
          json.SetNullStringValue('null')
          json.SetNullNumericValue(2415919001)
          
          json.Load(structure,'a.json')
        
        Note that you would need to parse, and manage these values in your
        structure (group, queue, table) but at least you can tell that they have
        been omitted.
        
        The properties for omitted and null are unrelated, you can use one set
        without the other set if you like.
        
      
      Loading a JSON List into a Group
       Typically if you have an incoming List of data you
        would load this into a matching Queue on the program site. It is however
        possible to use a Group structure, and have jFiles load that group
        structure, even if the incoming JSON contains a List structure. 
        
        For example;
        
        
Settings   Group
          Server       String(255)
          Port         Long
                     End
        
        this would usually expect JSON like this
        
        
{ 
              "Server":"www.capesoft.com",
              "Port":80
          }
        
        However the incoming JSON may occasionally be a list, like this
        
        
[{ 
                  "Server":"https://www.capesoft.com",
                  "Port":80
              },
              { 
                  "Server":"https://www.capesoft.com",
                    "Port":443
              }
          ]
        
        If the Load method was called with the Group as the destination
        structure then each item in turn will be loaded into the group,
        resulting in the last record in the JSON being left in the group when
        the Load completes.
        
        For each record loaded into the group the 
ValidateRecord
          method is called. So you can embed code in here if you want to
        iterate through the list. If the method then returns 
jf:StopHere
        then the loop will terminate and the Group will be left with the current
        contents.
        
        
json             Class(jsonClass)
          ValidateRecord     Procedure(),Long,Proc,Virtual
                           End
          
          json.ValidateRecord Procedure ()
            Code
            If inv:date < date(1,1,2018) then Return jf:filtered.
            Return jf:ok
      High-Speed Importing to Tables
       Importing JSON to a table is fast, but does take
        time. One of the techniques for speeding up imports is to surround the
        imports with a LOGOUT / COMMIT statement. 
        
        The price of this approach though is that the tables are locked for the
        duration of the import. This means that other users won't be able to
        write to the tables (and may not be able to read from the tables) while
        the import is underway. So the choice is fast imports, with possible
        user blocking, or slower imports, but with no user blocking.
        
        A property exists to turn this feature on (it is off by default).
        
        json.SetLogoutOnImport(x)
        
        Where x is either 1, or 2. If x is 1, then the Logout is attempted. If
        it fails, the import will continue anyway, but as a normal import, not a
        logout/commit import. If it is set to 2, and the logout fails, then the
        import is abandoned, and the method returns JF:ERROR.
        
        For maximum speed it is recommended to commit the transaction every y
        records. A good value for y is "several thousand" and so y defaults to
        5000. you can override this before the import by calling
        
        json.SetFlushEvery(6000)
        
        Setting it to 0 bypasses this feature, and only commits the transaction
        at the end of the entire import.
        
        One side effect of FlushEvery is that a new transaction is started every
        y records. This means is the original Logout failed (and x is 1), then
        it will be retried after y records. So even if the original logout
        fails, it may logout at a later part of the import.
      
      Renaming Fields on Load
      When a queue is nested inside a group or queue, then
        the 
NewPointer method is called so the
        derived procedure can NEW the necessary structure. The name of the
        structure is passed to NewPointer so that it can note the correct
        structure to NEW.
        
        A problem can occur when multiple structures, with the same name, are
        used in multiple places.
        
        
element       Queue
          name            string(100)
          value1          &ValueQueueType,name('value | queue')
          extra           &ExtraQueueType
                        End
          
          ExtraQueueType  Queue,Type
          comment           string(100)
          value 2           &ValueQueueType,name('value | queue')                End
        
        In this context the passed in value has to be unique so that it can be
        identified in NewPointer. NewPointer needs to distinguish between value1
        and value2.
        Since the name field is passed to NewPointer the structure becomes;
        
        
element         Queue
          name              string(100)
          value1            &ValueQueueType,name('value1 | queue')
          extra             &ExtraQueueType
                          End
          
          ExtraQueueType  Queue,Type
          comment           string(100)
          value 2           &ValueQueueType,name('value2 | queue') 
                     End
          
        
        This would have the effect though of breaking the import, since the JSON
        contains value, not value1 or value2. In this situation the RENAME
        attribute can be used. So the structure becomes;
        
        
element         Queue
          name              string(100)
          value1            &ValueQueueType,name('value1 | queue |
          rename(value)')
          extra             &ExtraQueueType
                          End
          
          ExtraQueueType  Queue,Type
          comment           string(100)
          value 2           &ValueQueueType,name('value2 | queue |
          rename(value)') 
                     End
          
        
        See Also : 
Renaming fields on Save
       
     For the purposes of this section it is assumed that
      the JSON object is declared as follows;
      
      json JSONClass 
      
      Reusing a JSON object
       If you reuse a json object multiple times then
        properties set in one use may inadvertently cascade to the next use. To
        "clean" an object so that it starts fresh, call the Start method. For
        example; 
        
        json.Start()
        
        This will reset the internal properties back to their default values.
      
      Saving a Clarion structure to a JSON File
        on disk
       The simplest way to create a JSON file is simply to
        use the .Save method and an existing Clarion structure.
        
        json.Save(Structure,<FileName>,
            <boundary>, <format>, <compressed>)  
        
        For example
        
        
json.Start()
          json.Save(CustomerTable,'.\customer.json')
        
        or
        
        
json.Start()
          json.Save(CustomerTable,'.\customer.json','Customers')
        
        or
        
        
json.Start()
          json.Save(CustomerTable,'.\customer.json','',json:Object)
        
        You can use a Group, Queue, View or File as the structure holding the
        data being saved. 
        
        The method returns 0 if successful, non-zero otherwise.
        
        The boundary parameter allows you to "name" the records. For example, if
        the boundary parameter is omitted the JSON is
        
        
[ { record }, {record} , ... {record} ]
        
        If the boundary is included then the JSON becomes
        
        
{ "boundary" : [ { record }, {record} , ...
          {record} ] }
          
        The Format property determines if the output is formatted to
        human-readable or if all formatting is removed (to make the file a bit
        smaller). If omitted it defaults to true - meaning that the output is
        human readable. This is recommended, especially while developing as it
        makes understanding the JSON and debugging your code a lot easier.
        
        If the Compressed parameter is omitted, then the default value is false.
        If the Compressed parameter is set to true then the file will be gzip
        compressed before writing it to the disk. 
        
        If the FileName parameter is omitted, or blank, then the json object
        will be populated with the file, but no disk write will take place. You
        can then save the object to a 
StringTheory
          object, or to a 
File, or use it
        in a 
collection later on.
        
      
Saving a Clarion structure to a JSON
        String in Memory
       json.Save(Structure,
            StringTheory, <boundary>, <format>) 
        
        This is the same as saving the JSON to a File, except that the second
        parameter is a StringTheory object not a string. 
        For example;
        
          str  StringTheory
            Code
            json.Start()
            json.Save(CustomerTable,str)
          
         For explanation of the 
Boundary and
        
Format parameters see the section 
above.
      
 
      Constructing JSON Manually
       In some cases constructing the correct Clarion
        structure may be difficult, or the structure itself may not be known at
        compile time.  In these situations you can use the low-level Add method
        to simply construct the JSON manually.
        
        The Add method takes 3 parameters, the name, the value, and the type of
        the item. In turn it returns a pointer to the node created. Using this
        pointer allows you to embed inside nodes as you create them. For
        example;
        
        {
            "company" : "capesoft",
            "location" : "cape town",
            "phone" : "087 828 0123",
            "product" : [
              { "name" : "jfiles" },
              { "quality" : "great" },
              { "sales" : "strong" }
            ]
          }
        
        In the above JSON there is a simply group structure, followed by a list
        containing a variable number of name/value pairs.
        The code to create the above could be written as;
        
        Json JSONClass 
          Product &JSONClass 
          KeyValue &JSONClass
          
          code
            json.start()
            json.add('company','capesoft')
            json.add('location','cape town')
            json.add('phone','087 828 0123')
            Product &= json.add('product','', json:Array) 
            KeyValue &= Product.add('','',json:Object)
            KeyValue.add('name','jfiles')
            KeyValue &= Product.add('','',json:Object)
            KeyValue.add('quality','great')
            KeyValue &= Product.add('','',json:Object)
            KeyValue.add('sales','strong')
        
        Of course this is just an example. Using the Add method, and the fact
        that it returns the node added, allow you to construct any JSON you
        like.
        
        That said, this method can be tedious, and making use of Clarion
        structures is often easier to manage in the long run. 
        
      
      Storing Multiple Items in a JSON
        object
       The Save methods described above are perfect for
        creating a simple JSON structure based on a simple Clarion Data Type.
        However there are times when you will need to create a single JSON
        object which contains multiple different elements (known as a
        Collection.)
        
        collection &JSONClass
        
        The collection is created using the CreateCollection method. 
        
        collection &=
          json.CreateCollection(<boundary>)
        
        If the boundary is omitted then a default boundary ("subSection")
        will be used.
        
        
        [Aside: You do not need to dispose the Collection object - that will be
        done for you when the json object disposes.]
        
        You can then use the Append method to add
        items to the collection. There are a number of forms of the Append
          Method.
        
        Append (File, <Boundary>)
          Append (Queue, <Boundary>)
          Append (View, <Boundary>)
          Append (Group, <Boundary>)
        
        As with the Save methods the Boundary parameter is optional and can be
        omitted. If the parameter is omitted then a default object name will be
        used.
        
        You can also 
        
        Append (Name, Value, <Type>)
        
        to add a single name value pair to the collection. The type is the JSON
        type of the Value. It should be one of
        
        json:String   EQUATE(1) 
          json:Numeric  EQUATE(2)
          json:Object   EQUATE(3)
          json:Array    EQUATE(4)
          json:Boolean  EQUATE(5)
          json:Nil      EQUATE(6)
        
        if the Type parameter is omitted then the default
          json:string is used.
        
        Here is a complete example;
        
        json        JSONClass
          collection  &JSONClass
            code
            json.Start()
            collection &= json.CreateCollection('Collection')
            collection.Append(Customer,'Customers') 
            collection.Append(Queue:Browse:1)
            collection.Append(MemoView)
        
        Once you have created your collection you can save it to Disk or String
        using the techniques described below.
      
      Saving a JSON Object to Disk
       After you have constructed the JSON object to your
        satisfaction, you may want to store it as a file. This can be done using
        the SaveFile method. For example; 
        
        json.SaveFile('filename',format)
        
        If the format parameter is set to true, then the file will be formatted
        with line breaks, and indentation (using tabs), suitable for a person to
        read.
        If the format parameter is false (or omitted) then the file will be kept
        as small as possible by leaving out the formatting.
        
      
      Saving a JSON Object to
        StringTheory
       Internally the JSON is stored as a collection of
        objects. To use the result in a program it must be turned into a String
        and stored in a StringTheory object. This is done by passing a
        StringTheory object to the SaveString method.
        For example;
        
        str  StringTheory
            code
            json.SaveString(str,format)
        
        Once in the StringTheory object it can then be manipulated, compressed,
        saved or managed in any way you like.
        
        If the format parameter is set to true, then the string will be
        formatted with line breaks, and indentation (using tabs), suitable for a
        person to read.
        If the format parameter is false (or omitted) then the string will be
        kept as small as possible by leaving out the formatting.
      
      Arrays
       JSON supports Arrays. They look something like this
        
        
        {"phone": [ "011 111 2345","011 123 4567"]}
        
        The square brackets indicate that the field ("phone") contains a list of
        values, ie an array.
        
        Clarion also supports arrays, using the DIM attribute. So creating
        fields like the one above is very straight-forward
        
        PhoneGroup  Group
          phone         string(20),DIM(5)
                      End
            code
            json.Save(PhoneGroup)
        
        This would result in JSON that looks like this
        
        {
            "PHONE" : ["012 345 6789","023 456 7890"]
          }
        
        Empty items in the array, which come after the last set value, are
        suppressed. In the above example, only the first two array values were
        set, so only 2 values were included in the output. Position in the array
        is preserved. If phone[1] and phone[3] were set, but phone[2] was left
        blank then the output would read
        
        {
            "PHONE" : ["012 345 6789","","023 456 7890"]
          }
        
        Items are considered empty if the field is a string, and contains no
        characters, of if the field is a number and contains a 0.
        
        Clarion supports multi-dimensional arrays. These are written out as if
        they were a single dimension array.
        For example;
        
        Matrix[1,1] = 1010
          Matrix[2,1] = 2010
          Matrix[1,2] = 1020
          Matrix[2,2] = 2020
        
        becomes
        
        "MATRIX" : [1010,1020,2010,2020]
        
        Note that variables of DIM(1) are not
        allowed. To be an array the dimension value must be greater than 1.
        
        It is possible (and valid) for an entire JSON file to consist of a
        single array.
        
        [1,2,3,4,5]
        
        In this case there is neither a field name, nor an array or object
        wrapper around the value.
        
        dude long,dim(5) 
            code
            dude[1] = 12
            dude[2] = 22
            dude[3] = 43
            json.Start() 
            json.SetType(json:Null)  
            json.AddArray('',dude)   
        
        this results in
        
        [12,22,43]
        
        As noted earlier, trailing empty values (0 if a number, blank if a
        string) are removed.
        
      
      Blobs
       Creating JSON files from Tables or Views that
        contain Blobs are supported, but unfortunately coverage can vary a bit
        based on the driver in use.
        Tables
         Creating JSON using any of the Save(Table...) ,
          Add(Table...) or Append(Table...) methods is supported. 
          If the table has memo or blob fields then these are included in the
          export, and there is nothing specific you need to do.
          
          If you wish to suppress MEMOs and BLOBs when saving a table set the NoMemo property to true. For example
          
          json.start()
            json.SetNomemo(True)
            json.save(...)
        
        Views
         Clarion Views behave differently when using
          TopSpeed or SQL drivers. So if you need to save a BLOB with a VIEW
          then read this section carefully.
          
          For all drivers, VIEWs can contain BLOB fields. For example;
          
          ViewProduct View(Product)
                          Project(PRO:Name)
                          Project(PRO:RRP)
                          Project(PRO:Description) 
                        End
          
TopSpeed
          The TopSpeed driver is able to detect the BLOB fields in the VIEW, and
          so there's no extra code for you to do.
          
          Note 1: If the NoMemo property is set to
          true then the BLOB will be suppressed
          even if it is in the VIEW.
          
          Note 2 : The shorthand method of projecting all fields in a table, by
          projecting no fields, does not include BLOB or MEMO fields. If you
          want to PROJECT MEMOS or BLOBs then you must PROJECT it (and all other
          fields) explicitly.
          SQL
          The SQL drivers are unable to detect BLOB fields in the VIEW
          structure. The BLOBS are still populated, but the explicit method to
          determine if the BLOB is in the VIEW or not, does not work for SQL
          Drivers.
          
          jFiles adopts the following work-arounds to this issue.
          
          a) [Default behavior]. When looping through the VIEW all the BLOB
          fields are checked for content. If the value is not blank then it is
          included in the output. In other words BLOBs with content are
          exported, BLOBs without content are not included. In most cases this
          will likely be sufficient as JSON allows fields to be excluded when
          they are blank. 
          
          b) If all the BLOBs from the Table are included in the VIEW then you
          can set the property  ExportBlobsWithView to
          true. If this value is true then all the
          BLOBs will be included in each JSON record. If they are blank (or not
          included in the VIEW) then they will be included in the JSON as blank.
          
          
          So in order to export BLOBs with VIEW records set the property ExportBlobsWithView to true.  For example;
          
          ViewProduct View(Product)
                           Project(PRO:Name)
                           Project(PRO:RRP)    
                           Project(PRO:Description)
            ! This is a blob
                        End
            
            json.Start()
            json.SetExportBlobsWithView(True)
            json.Save(ViewProduct)
          
          Note1: If the NoMemo property is set to
          true then the memos and blobs will not be included even if ExportBlobsWithView
            is set to true.
      Creating Nested JSON structures from a View
       jFiles 2 introduces the ability to create nested
        JSON structures from a VIEW declaration.  Consider this VIEW;
        
        Invoice_view View(Invoice)
                          Project(INV:ID) 
                          Project(INV:Date)
                          Project(INV:Paid)
                          Join(LIN:InvKey,INV:ID) ,INNER 
                              Project(LIN:ID)
                              Project(LIN:Quantity)
                              Project(LIN:Price)
                              Join(PRO:Key,LIN:Product) ,INNER 
                                  Project(PRO:Name)
                              End
                          End 
                       End
        
        This View exports all the details for an invoice, including the invoice
        details, the lineitem details, and the product details.
        
        The ideal output for a view like this is something like this;
        
        
{ "Invoices":[{
              "ID":1,
              "Date":"2019/7/1",
              "Paid":1,
              "Line Items":[{
                  "ID":1001,
                  "Quantity":5,
                  "Price":100,
                  "Product":{
                      "Name":"jFiles"
                  }
              }]
          }]}
                  
        
        With jFiles 2 creating a nested structure like this directly from the
        view is possible. There are only two extra bits of code required. The
        example code looks like this;
        
        
json.start() 
          json.setTagCase(jf:caseAsIs) 
          json.SetNestViewRecords(true) ! [1]
          json.setViewBoundary(Invoice_view,LineItems,'Line
          Items',jf:OneToMany)  ! [2]
          json.setViewBoundary(Invoice_view,Product,'Product',jf:ManyToOne)      
          ! [3]
          json.save(Invoice_view,str,'Invoices')
        
        The first extra line of code is [1], this sets the 
NestViewRecords
        property and this tells jFiles that you want a nested VIEW structure.
        
        The calls to 
SetViewBoundary tell jFiles
        a little bit more about the JOIN itself, and allows you to specify the
        child tag as well.
        
      
Creating Nested JSON structures from a
        Group or Queue
       Clarion Group and Queue structures can contain
        complex field types. Thanks to 
Extended
          Name Attribute support it is now possible to send complex groups
        and queues to JSON, without having to embed code in derived methods. For
        example;
        
        
CustomerQueue     Queue,pre(cust)
          Name                 string(100),name('Name')
          Invoices             &InvoiceQueueType,name('Invoices| Queue')
                            End
          
          json.start()
          json.SetTagCase(jf:CaseAsIs)
          json.Save(CustomerQueue,str)
        
        In the above example a Queue is inside another queue - each record in
        the parent contains a complete queue. In the JSON output the internal
        queue is nested inside the parent record.
        
        you can do the same thing with a Group, for example exporting two
        different tables at the same time;
        
        
backup          Group
          customers         &File,name ('customers | table')
          products          &File,name ('products | table')
                          End
          
          json.start()
          json.SetTagCase(jf:CaseAsIs)
          json.Save(backup,'backup.json')
        
        
      Creating Nested JSON structures
       This section follows on from the 
          Storing Multiple Items in a JSON Object section above.
        
        Another form of the Append method exists, which allows you to start a
        new collection within your collection.
        
        
Append(<Boundary>)
        
        This starts a new collection inside an existing collection. To use this,
        first you need to declare a pointer to this collection;
        
        
subItem   &JSONClass
        
        Then (after doing the 
CreateCollection call
        and so on) you can do
        
        
subItem &=
          Collection.Append('VersionInformation')
        
        and after that do as many 
subItem.Appends
        as you like.
        
        This nesting can continue to as many levels as you like.
        
        Here is a complete example;
        
        
json        JSONClass
          collection  &JSONClass
          subItem     &JsonClass
            code
            json.Start()
            collection &= json.CreateCollection('Collection')
            collection.Append(Customer,'Customers') 
            collection.Append(Queue:Browse:1)
            collection.Append(MemoView)
            subItem &= Collection.Append('VersionInformation')
            subItem.Append('Version','6.0.3')
            subItem.Append('Build',1234,json:numeric)  
      Formatting Field Values on Save
       Up to now all the exporting of the fields has
        resulted in the raw data being stored in the JSON file. In some cases
        though it is preferable to export the data formatted in some way so that
        it appears in the JSON as a more portable value. For example in Clarion
        Dates are stored as a LONG, but if the data needs to be imported into
        another system then displaying the date as 
yyyy/mm/dd
        might make the transfer a lot easier. 
        
        This can be done by adding the desired Picture to the NAME attribute of
        the field. For example;
        
        
InvoiceQueue Queue
          Date Long,name('Date | @D3')
          End
        
        All 
 StringTheory Extended Pictures are supported. For
        more information on the name attribute see 
Extended
          Name Attributes.
        
        
jFiles 1
         The process above is considerably simpler than the
          one below, and should be used wherever possible. If it is not
          possible, or you are reading old code (and trying to understand it)
          then the Files 1 method is described below. 
          
          This is done by embedding in the FormatValue method
          in your json class. The method is declared as;
          
          json.FormatValue PROCEDURE (String pName, String
            pValue, *LONG pLiteralType),String
          
          Note the LiteralType parameter. If you are changing the type of the
          data (for example, changing the DATE from a Numeric to a String) then
          you need to change the LiteralType value as well. The value of this
          parameter should be one of
          
          json:String   EQUATE(1) 
            json:Numeric  EQUATE(2)
            json:Object   EQUATE(3)
            json:Array    EQUATE(4)
            json:Boolean  EQUATE(5)
            json:Null     EQUATE(6)
          
          As the name of the field is passed into the method, it is
          straight-forward to create a simple CASE statement formatting the
          fields as required. This code is embedded before the parent call. Also
          note that the value in pName is the JSON field name - not the Clarion
          field name. And this value is case sensitive.
          
          case pName
            of 'DATUM'
              pLiteralType = json:string
              Return clip(left(format(pValue,'@d1')))
            of 'TIME'
              pLiteralType = json:string 
              Return clip(left(format(pValue,'@t4')))
            end
          
          In the above case the Datum and Time fields are formatted, all other
          fields are left alone and placed in the file "as is".
        
      Renaming Fields on Save
       When exporting JSON from a structure the External
        Name of each field is used as the "Tag" : name in the JSON. For example
        
        xQueue       Queue
          field1         string(255),Name('Total')
                       End
        
        results in JSON like this;
        
        
{"Total" : "whatever"}
        
        Ideally the external Name attribute of the field contains the correct
        value for the tag. 
        There are times however when you need to override this. You can do this
        by adding an attribute to the NAME attribute. The RENAME and JSONNAME
        attributes are supported. For example;
        
        
xQueue       Queue
          field1         string(255),Name('Total | JsonName(Extra Total)')
          field2         string(255),Name('SubTotal | Rename(Sub Total)')
                       End
        
        For more information on the name attribute see 
Extended
          Name Attributes. See also 
Renaming
          Fields on Load.
        
jFiles 1
         The process above is considerably simpler than the
          one below, and should be used wherever possible. If it is not
          possible, or you are reading old code (and trying to understand it)
          then the jFiles 1 method is described below. 
          
          In jFiles 1 this is done by embedding code into the AdjustFieldName
          method, AFTER (or BEFORE) the PARENT call.
          
          Example
          
          json.AdjustFieldName PROCEDURE (StringTheory
            pName, Long pTagCase)
              CODE
              PARENT.AdjustFieldName (pName,pTagCase)
              case pName.GetValue()
              of 'Total'
                pName.SetValue('Totalizer')
              End 
          
          Note that the field name in the above CASE statement is a
          case-sensitive match. If you need a case insensitive match then UPPER
          or LOWER both the CASE and OF values.
          
          The Parent call performs the replaceColons and remove prefix work. So,
          if you put the CASE before the parent call, then the field will come
          in "Clarion Style", if after the parent call then "JSON Style". It is
          up to you which side of the Parent call you put your code onto. 
      Saving Nested Structures -
        another approach
       Consider the following JSON;
        
        {  "customer" : {
                  "Name" : "Bruce",
                  "Phone" : "1234 567 89",
                  "Invoices" : [
                      {
                          "InvoiceNumber" : 1,
                          "LineItems" : [
                              {
                                  "Product" : "iDash",
                                  "Amount" : 186.66
                              }
                          ]
                      },
                      {
                          "InvoiceNumber" : 2,
                          "LineItems" : [
                              {
                                  "Product" : "Runscreen",
                                  "Amount" : 179.75
                              }
                          ]
                      }
                  ]
              }
          }
        
        This is constructed from a Group (Customer information) which contains a
        Queue (of Invoices) and each invoice contains a Queue of Line Numbers.
        It's worth pointing out that the line items queue is a simple JSON form
        of a queue, and the Invoice Queue is again just a JSON form of a queue.
        
        The Clarion structures for the above are as follows;
        
        CustomerGroup   Group
          Name              string(50),name('Name')
          Phone             string(50),name('Phone')
                          End
          
          InvoicesQueue   Queue
          InvoiceNumber     Long,name('InvoiceNumber')
                          End
          
          LineItemsQueue  Queue
          Product           String(50),name('Product')
          Amount            Decimal(8,2),name('Amount')
                           End
        
        In this case the structures are a Group and Queues, but you could also
        use Views or Tables if you wanted to.
        
        In order to achieve the result three jFiles objects are used;
        
        CustomersJson Class(JSONClass)
          AssignValue     PROCEDURE (JSONClass pJson,StringTheory pName,|
                                      *Group pGroup,*Long pIndex,Long
          pColumnOffset),VIRTUAL
                        End
          InvoicesJson  Class(JSONClass)
          AssignValue     PROCEDURE (JSONClass pJson,StringTheory pName, |
                                     *Group pGroup,*Long pIndex,Long
          pColumnOffset),VIRTUAL
                        End 
          LineItemsJson JSONClass
        
        As you can see two of the classes (the ones that have children) will
        have some override code in the  AssignValue method.
        (More on that in a moment.)
        
        For the purposes of this example, the code for populating the structures
        is omitted.
        
        The basic code to generate the JSON file looks like this;
        
        
          CustomersJson.Start()
          CustomersJson.SetTagCase(jF:CaseAsIs)
          CustomersJson.Save(CustomerGroup,'customer.json','customer',true)
        
        In order to include the InvoicesQueue inside the group some code is
        added to the CustomersJson.AssignValue
        method. The code looks like this;
        
        CustomersJson.AssignValue PROCEDURE (JSONClass
          pJson,StringTheory pName,|
                                               *Group pGroup,*Long pIndex, Long
          pColumnOffset)
            Code
            PARENT.AssignValue (pJson,pName,pGroup,pIndex,pColumnOffset)
            If pName.GetValue() = 'Phone'
              do PrimeInvoicesQueue 
              InvoicesJson.Start()
              InvoicesJson.SetTagCase(jF:CaseAsIs)
              InvoicesJson.Save(InvoicesQueue, ,'Invoices')  
              pJson.AddCopy(InvoicesJson, ,true) 
            End
        
        There are a few interesting things to note in the above code.
        
        a) Notice it's checking for the JSON tag 'Phone'
        as it appears in the JSON file. This is simply the position in which the
        Invoice queue will be injected. As it is after the parent call, it will
        come after the Phone field in the JSON
        file. If it was before the parent call it would come before the Phone
          field. If it came before the parent call, and the parent was
        not called at all, then the Phone field
        would be excluded from the JSON.
        
        b) The code to Prime the Queue, and Save that Queue to the InvoicesJson
          object is standard code as described earlier in this document.
        Notice the omitted parameter in the call to .Save.
        
        cc) The AddCopy call is where the magic
        happens. This adds a copy of the  InvoicesJson object
        into the  CustomersJson object at the
        position specified by the passed in parameter pJson.
        
        
        d) The parameters pGroup, pIndex
          and  pColumnOffset are not useful
        in your embed code, they are used in the call to the parent method.
        
        As this example covers three layers of JSON, the technique is repeated
        for the InvoicesJson  object. It too has
        an AssignValue  method, and it uses
        similar code to inject the LineItems at
        that point;
        
        InvoicesJson.AssignValue PROCEDURE (JSONClass
          pJson,StringTheory pName,|
                                             *Group pGroup,*Long pIndex,Long
          pColumnOffset)
            CODE
            PARENT.AssignValue (pJson,pName,pGroup,pIndex,pColumnOffset)
            if pName.GetValue() = 'InvoiceNumber'
              do PrimeLineItemsQueue
              LineItemsJson.Start()
              LineItemsJson.SetTagCase(jF:CaseAsIs)
              LineItemsJson.Save(LineItemsQueue, ,'LineItems')
              pJson.AddCopy(LineItemsJson)
            End
        
        
        
      
      Saving Nested Structures - yet
        another approach
       Consider the following JSON;
        
        
          [
	{
		"_id" : "7123098",
		"accountName" : "Charlies Plumbing",
		"mainContact" : "",
		"mainPhone" : "",
		"accountLogins" : [
			{
				"loginName" : "Administrator",
				"loginPwd" : "secret",
				"loginHistory" : [
					{
						"loginDate" : "2017/07/17",
						"loginTime" : "16:27"
					},
					{
						"loginDate" : "2017/07/18",
						"loginTime" : "15:26"
					}
				]
			},
			{
				"loginName" : "Operator",
				"loginPwd" : "1234",
				"loginHistory" : [
					{
						"loginDate" : "2017/07/17",
						"loginTime" : " 8:15"
					},
					{
						"loginDate" : "2017/07/18",
						"loginTime" : "15:51"
					}
				]
			}
		],
		"accountContacts" : [
			{
				"contactName" : "Beatrice",
				"contactPosition" : "CEO"
			},
			{
				"contactName" : "Timothy",
				"contactPosition" : "Sales"
			}
		]
	}
]
        
        This is a highly nested structure. It is an AccountsQueue, which in turn
        contains a Logins Queue and a Contacts Queue. The Logins Queue contains
        a Login History queue.
        
        Here is the Accounts queue declaration;
        
        
AccountsQueue   Queue 
          id                string(20),name('_id')
          accountName       string(255),name('accountName') 
          mainContact       string(255),name('mainContact')
          mainPhone         string(255),name('mainPhone')
          accountLogins     &accountLoginsQueue,name('accountLogins')
          accountContacts   &accountContactsQueue,name('accountContacts')
                          End
        
        The Contacts queue declaration
        
        
accountContactsQueue Queue,type
          contactName             string(100),name('contactName')
          contactPosition         string(100),name('contactPosition')
                               End
        
        The Logins queue
        
        
accountLoginsQueue Queue,type
          loginName             string(100),name('loginName')
          loginPwd              string(100),name('loginPwd') 
          loginHistory          &loginHistoryQueue,name('loginHistory')
                             End
        
        and finally the History queue
        
        
loginHistoryQueue Queue,type
          loginDate           string(10),name('loginDate')
          loginTime           string(10),name('loginTime')
                            End
        
        Populating nested queues has to be done carefully. The queue pointers
        are allocated using the NEW statement whenever a record is created. Here
        is a single record added to the Accounts queue (with various child queue
        entries added as well.)
        
        
clear(AccountsQueue)
          AccountsQueue.accountLogins &= new(accountLoginsQueue) 
          AccountsQueue.accountContacts &= new(accountContactsQueue)
          
        AccountsQueue.id = '7123098'
          AccountsQueue.AccountName = 'Charlies Plumbing'
          
         AccountsQueue.accountLogins.loginName =
          'Administrator'
          AccountsQueue.accountLogins.loginPwd = 'secret'
          AccountsQueue.accountLogins.loginHistory &= new loginHistoryQueue
          AccountsQueue.accountLogins.loginHistory.loginDate =
          format(today()-1,@d10)
          AccountsQueue.accountLogins.loginHistory.loginTime =
          format(random(360000*8, 360000*18),@t1)
          add(AccountsQueue.accountLogins.loginHistory)
          
          AccountsQueue.accountLogins.loginHistory.loginDate =
          format(today(),@d10)
          AccountsQueue.accountLogins.loginHistory.loginTime =
          format(random(360000*8, 360000*18),@t1)
          add(AccountsQueue.accountLogins.loginHistory)
          
          add(AccountsQueue.accountLogins)
          
          AccountsQueue.accountLogins.loginName = 'Operator'
          AccountsQueue.accountLogins.loginPwd = '1234'
          AccountsQueue.accountLogins.loginHistory &= new loginHistoryQueue
          
          AccountsQueue.accountLogins.loginHistory.loginDate =
          format(today()-1,@d10)
          AccountsQueue.accountLogins.loginHistory.loginTime =
          format(random(360000*8, 360000*18),@t1)
          add(AccountsQueue.accountLogins.loginHistory)
          
          AccountsQueue.accountLogins.loginHistory.loginDate =
          format(today(),@d10)
          AccountsQueue.accountLogins.loginHistory.loginTime =
          format(random(360000*8, 360000*18),@t1)
          add(AccountsQueue.accountLogins.loginHistory)
          
          add(AccountsQueue.accountLogins)
          
         AccountsQueue.accountContacts.contactName =
          'Beatrice'
          AccountsQueue.accountContacts.contactPosition = 'CEO'
          add(AccountsQueue.accountContacts)
          AccountsQueue.accountContacts.contactName = 'Timothy'
          AccountsQueue.accountContacts.contactPosition = 'Sales'
          add(AccountsQueue.accountContacts)
          
          Add(AccountsQueue) ! save the queue record.
        
        Sending even a complex structure like to this to JSON is relatively
        easy.
        
        First the JSON object is declared. You can do this in code, or let the
        extension template declare it for you. Notice the 
AddByReference
          method, that will be fleshed out in a moment.
        
        
json             Class(JSONClass)
          AddByReference     PROCEDURE (StringTheory pName,JSONClass
          pJson),VIRTUAL
                           End 
        
        Secondly the json object is called as normal;
        
        
json.Start()
          json.SetTagCase(jF:CaseAsIs)
          json.SetColumnType('accountLogins',jf:Reference)  json.SetColumnType('loginHistory',jf:Reference)
          json.SetColumnType('accountContacts',jf:Reference)
          json.Save(AccountsQueue,'json.txt')
        
        Notice the extra calls to 
SetColumnType.
        These tell the class that these fields are reference values, and so need
        to be saved separately. 
        
        The final step is to flesh out the AddByReference method. When the class
        encounters one of these reference fields it calls the 
AddByReference
          method. The code in there looks something like this;
        
        
json.AddByReference PROCEDURE (StringTheory
          pName,JSONClass pJson)
            CODE
            case pName.GetValue()
            of 'accountLogins'
              pJson.Add(AccountsQueue.accountLogins)
            of 'accountContacts'
              pJson.Add(AccountsQueue.accountContacts)
            of 'loginHistory' 
              pJson.Add(AccountsQueue.accountLogins.loginHistory)
            end
            PARENT.AddByReference (pName,pJson)
        
        Remember the tag names are case sensitive so be careful entering them
        here.
        
        
Disposing Nested Queues
         This section has nothing to do with jFiles, but
          since the above example shows how to load a nested Queue structure,
          it's probably worth covering the Disposal of nested queue structures
          here. If disposal is not done correctly then a memory leak will occur.
          
          
          The key lines to worry about in the above code are;
          
          AccountsQueue.accountLogins &=
            new(accountLoginsQueue) 
            AccountsQueue.accountContacts &= new(accountContactsQueue)
          
          and
          
           AccountsQueue.accountLogins.loginHistory &=
            new loginHistoryQueue
          
          These lines are creating queues on the fly, and each call to new MUST
          have a matching call to dispose. When deleting a row from
          AccountsQueue or AccountsQueue.AccountLogins (and that includes
          deleting all rows) the child queues themselves must first be disposed.
          It's important to manually do this before the procedure ends - it will
          not be done automatically.
          
          the basic idea is to loop through the queue, deleting the sub queues
          as you go.
          
          For example;
          
          Loop While Records(AccountsQueue)
              Get(AccountsQueue,1)
              Loop while records(AccountsQueue.accountLogins)
                Get(AccountsQueue.accountLogins,1)
                Free(AccountsQueue.accountLogins.loginHistory)
                Dispose(AccountsQueue.accountLogins.loginHistory)
                Delete(AccountsQueue.accountLogins)
              End
              Free(AccountsQueue.accountLogins)
              Dispose(AccountsQueue.accountLogins)
              Free(AccountsQueue.accountContacts)
              Dispose(AccountsQueue.accountContacts)
              Delete(AccountsQueue)
            End
           
       When creating a JSON file, or loading a JSON file into
      a structure, it is necessary to match the field names in the JSON file
      with the field names in your structure. There are properties which assist
      in making a good match.
      
      These properties can be set to default values via the global extension,
      the local extension, or can be set in embed code before the object is
      used. These properties are not reset by a call to 
json.Start().
      
      
Note that all the properties should be
      set using their 
SET method, and retrieved
      using their 
GET method. For example setting
      the 
RemovePrefix property is done using the
      
SetRemovePrefix(whatever) method. And it can
      be retrieved using the 
GetRemovePrefix()
      method.
      
RemovePrefix
       Clarion structures allow for the use of prefixes,
        which then form part of the field name. If this property is set when you
        create JSON then the prefix (and colon) are omitted from the JSON and
        only the "name" part of the fieldname is used.
        
        IF you are importing JSON, and the JSON was created by another entity,
        then it's likely the fields in the JSON are not prefixed. In that case
        you should set this property to true as well, so that the matcher
        matches on names-without-prefixes. 
      PrefixChars
       In Clarion a colon (:) character is used to separate
        the prefix from the field name. Incoming JSON may be using an alternate
        character (often an underscore(_) ) to separate the prefix from the rest
        of the name.
        
        To complicate the issue colons, and underscores, are valid characters in
        Clarion field names, table names, and prefixes. If you do have colons or
        underscores in the name then that brings MaxPrefixLengthInJSON into
        play.
      
      MaxPrefixLengthInJSON 
       To make identifying a prefix easier, it can be
        helpful to tell jFiles about the length of any expected prefix. So if
        the length of all your prefixes are say 3 characters, then you should
        set this value to 4. (3 for the prefix, plus one for the separator.) Any
        separators in the string AFTER this length will not be treated as a
        prefix separator.
      ReplaceColons
       Colons are a legal character in Clarion field names.
        However in most languages they are not. Therefore to create JSON which
        is portable into other systems it may be necessary to replace any colons
        with some other character (or characters) - most usually an underscore
        character. If you are including the prefix in the name then this setting
        becomes doubly important.  The default value of this property is true.
      ReplacementChars
       The default replacement character for a colon is an
        underscore character. However if you wish to replace it with some other
        combination of characters then you can set this property to whatever you
        like, up to 10 characters long.
      
      TagCase
       JSON is explicitly case sensitive. When creating
        JSON you can control the case using the TagCase
        property. Valid values for this property are;
        
        jf:CaseUpper
          jf:CaseLower
          jf:CaseAsIs
          jf:CaseAny
        
        As the equates suggest, CaseUpper forces
        all the tags to be uppercase, CaseLower forces
        all the tags to be lower case, and CaseAsIs uses
        the case as set in the fields External Name. (If there is no External
        Name for a field then Upper case is used.)
        CaseAny is only used on a Load.
        It matches incoming node names to local field names regardless of case.
      
      Labels vs Names
       In Clarion fields (fields in a table, queue or
        group, or just variables by themselves) have a 
label, which is
        the identifier in column 1 of the source code.
        This is not the 
name of the field (although they are often
        called "
Field Names". The 
Name of a field only exists
        if you have set the 
,Name property for the
        field. Since Clarion is a case insensitive language all labels are seen
        as UPPER case by the compiler. 
        
         If you are unclear on this please see 
 ClarionHub.
        
        So when importing make sure you understand this point, especially when
        setting the 
TagCase property as mentioned
        above.
      
 The JSON object can be thought of as a tree. The root
      JSON object contains other JSON objects, and those ones contain other ones
      and so on. This is a very elegant approach to the code, but it does have
      one drawback - code embedded in the methods of the root object (ie the
      object in your procedure) does not get called when a method on one of the
      child objects is called.
      
      This means that embedding code in most of the methods will not be useful
      because it will not execute when you expect it to. However some methods
      will execute and are suitable for adding embed code. They are;
      
      AddByReference
        AddQueueRecord
        AdjustFieldName
        AssignField
        AssignMissingField
        AssignValue
        DeformatValue
        ErrorTrap
        FormatValue
        InsertFileRecord
        SetColumnType
        Trace
        UpdatefileRecord
        ValidateField
        ValidateRecord
       
      In addition many methods are called only by your program so are suitable
      for embedding. They are
      
      Start
        CreateCollection
        Save  (any form), SaveFile, SaveString
        Load  (any form), LoadFile, LoadString
        Append (any form)
       
      
    
     Run the supplied installation file. 
     This product is supplied as source files that are
      included in your application. There are no additional files for you to add
      to your distribution.