Order of JSON items

Just starting out? Need help? Post your questions and find answers here.
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Order of JSON items

Post by Wolfram »

Why is the order of the items in this example different from the order I see in a text editor?

Code: Select all

Global NewList packages.s()

Declare findIsMandatory(JSONValue, getMembers=#NO)


Procedure.i findIsMandatory(JSONValue, getMembers=#NO)
  Protected key$
  
  
  Select JSONType(JSONValue)
    Case #PB_JSON_Object
      
      If ExamineJSONMembers(JSONValue)
        While NextJSONMember(JSONValue)
          Select JSONType(JSONValue)
            Case #PB_JSON_Number
;           Debug JSONMemberKey(JSONValue) + " = " + GetJSONInteger( JSONMemberValue(JSONValue) )
            Case #PB_JSON_String
;           Debug JSONMemberKey(JSONValue) + " = " + GetJSONString( JSONMemberValue(JSONValue) )
              
            Case #PB_JSON_Boolean
;           Debug JSONMemberKey(JSONValue) + " = " + GetJSONBoolean( JSONMemberValue(JSONValue) )
              
            Case #PB_JSON_Object
              key$ = JSONMemberKey(JSONValue)
              
              If getMembers
                AddElement( Packages() )
                packages() = key$
                         
              Else          
                If key$ = "Packages"
                  FindIsMandatory( JSONMemberValue(JSONValue), #YES)
                EndIf
                
              EndIf
              
          EndSelect
        Wend
      EndIf
      
      
;     Case #PB_JSON_Array
      
  EndSelect
  
  ProcedureReturn #False
EndProcedure


Define JSON$, JSONRoot


fd = ReadFile(#PB_Any, "/myFile.json")

If fd
  length = Lof(fd)
  
  JSON$ = ReadString(fd, #PB_UTF8 | #PB_File_IgnoreEOL, length)
  
  CloseFile(fd)
  
  If ParseJSON(0, JSON$)
    JSONRoot = JSONValue(0)
    FindIsMandatory(JSONRoot)
  Else
    Debug "err JSON"
  EndIf
  
  If ListSize( Packages() )
    ForEach packages()
      Debug packages()
    Next
  EndIf
  
EndIf
macOS Catalina 10.15.7
Little John
Addict
Addict
Posts: 4775
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Order of JSON items

Post by Little John »

Little John wrote:A JSON object by definition is an unordered collection of name/value pairs.
PureBasic arranges the members of JSON objects in an unpredictable way. It's not possible to control their order.
Solution: Use my JSave module. :-)
User avatar
kenmo
Addict
Addict
Posts: 2032
Joined: Tue Dec 23, 2003 3:54 am

Re: Order of JSON items

Post by kenmo »

Unfortunately the PB JSON library does not preserve the order of object members. I assume it's internally using a Map for object members, which does not preserve order.

Technically this is acceptable because the JSON spec says objects are unordered. Functionally it shouldn't matter.
But most users and some applications expect the fields not to move around!

See for example this thread from 2018
viewtopic.php?p=518165
(If you need another solution besides Little John's, check out "OJSON" linked in that thread.)


At this point maybe we could sponsor an official reworking of the library to preserve order :) (I'm serious!)
Quin
Addict
Addict
Posts: 1122
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Order of JSON items

Post by Quin »

kenmo wrote: Tue Jul 08, 2025 10:14 pm At this point maybe we could sponsor an official reworking of the library to preserve order :) (I'm serious!)
I'd contribute to this for sure. Probably behind a flag, but still.
Sergey
User
User
Posts: 53
Joined: Wed Jan 12, 2022 2:41 pm

Re: Order of JSON items

Post by Sergey »

I'm used a pointer to json object in memory (from min to max), maybe this help you order json members :wink:
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Re: Order of JSON items

Post by Wolfram »

Sergey wrote: Thu Jul 10, 2025 7:09 pm I'm used a pointer to json object in memory (from min to max), maybe this help you order json members :wink:
Sounds good, but how can I get the address of the object if I use NextJSONMember() ?
macOS Catalina 10.15.7
Sergey
User
User
Posts: 53
Joined: Wed Jan 12, 2022 2:41 pm

Re: Order of JSON items

Post by Sergey »

Code: Select all

Input$ = "{ " + Chr(34) + "x" + Chr(34) + ": 10, " + 
	Chr(34) + "y" + Chr(34) + ": 20, " + 
	Chr(34) + "z" + Chr(34) + ": 30 }"

ParseJSON(0, Input$)
ObjectValue = JSONValue(0)

If ExamineJSONMembers(ObjectValue)
	While NextJSONMember(ObjectValue)
		Debug Str(JSONMemberValue(ObjectValue)) + " <- " + JSONMemberKey(ObjectValue) + " = " + GetJSONInteger(JSONMemberValue(ObjectValue))		
	Wend
EndIf
From standard examples, every member has 40 bytes length
(see first values before <- mark, every time they will be different)
You need only sort by this values and all be good!
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Re: Order of JSON items

Post by Wolfram »

Hello Sergey,

your example does not work in my case.
I want to get the name in the same order as it is written, but I get Tom, Michel, Peter.

Code: Select all

Global NewList packages.s()


Declare findUser(JSONValue, getMembers=#NO)


Procedure.i findUser(JSONValue, getMembers=#NO)
  Protected key$
  
  
  Select JSONType(JSONValue)
    Case #PB_JSON_Object
      
      If ExamineJSONMembers(JSONValue)
        While NextJSONMember(JSONValue)
          Select JSONType(JSONValue)
            Case #PB_JSON_Number
                        Debug JSONMemberKey(JSONValue) + " = " + GetJSONInteger( JSONMemberValue(JSONValue) )
            Case #PB_JSON_String
                        Debug JSONMemberKey(JSONValue) + " = " + GetJSONString( JSONMemberValue(JSONValue) )
              
            Case #PB_JSON_Boolean
                        Debug JSONMemberKey(JSONValue) + " = " + GetJSONBoolean( JSONMemberValue(JSONValue) )
              
            Case #PB_JSON_Object
              key$ = JSONMemberKey(JSONValue)
              
              If getMembers
                AddElement( Packages() )
                packages() = key$
                
              Else          
                If key$ = "user"
                  findUser( JSONMemberValue(JSONValue), #YES)
                  
                EndIf
                
              EndIf
              
          EndSelect
        Wend
      EndIf
      
      
;     Case #PB_JSON_Array
      
  EndSelect
  
  ProcedureReturn #False
EndProcedure


Define JSON$, JSONRoot


JSON$ = "{" +#DQUOTE$  +"user" +#DQUOTE$  +": {" +#DQUOTE$  +"Peter" +#DQUOTE$  +": {" +#DQUOTE$  +"age" +#DQUOTE$  +": 33," +#DQUOTE$  +"size" +#DQUOTE$  +": 177," +#DQUOTE$  +"hobby" +#DQUOTE$  +": " +#DQUOTE$  +"sport" +#DQUOTE$  +"}," + 
        #DQUOTE$  +"Tom" +#DQUOTE$  +": {" +#DQUOTE$  +"age" +#DQUOTE$  +": 40," +#DQUOTE$  +"size" +#DQUOTE$  +": 180," +#DQUOTE$  +"hobby" +#DQUOTE$  +": " +#DQUOTE$  +"music" +#DQUOTE$  +"}," + 
        #DQUOTE$  +"Michel" +#DQUOTE$  +": {" +#DQUOTE$  +"age" +#DQUOTE$  +": 39," +#DQUOTE$  +"size" +#DQUOTE$  +": 184," +#DQUOTE$  +"hobby" +#DQUOTE$  +": " +#DQUOTE$  +"sport" +#DQUOTE$  +"}}}"



If ParseJSON(0, JSON$)
  JSONRoot = JSONValue(0)
  findUser(JSONRoot)
Else
  Debug "err JSON"
EndIf

If ListSize( packages() )
  ForEach packages()
    Debug packages()
  Next
EndIf
macOS Catalina 10.15.7
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: Order of JSON items

Post by Mijikai »

Interesting, here is what i came up with.

Code: Select all

EnableExplicit

Structure _JSON_MEMBER
  *member
  key.s
EndStructure

Structure _JSON_OBJECT
  count.i
  Array json._JSON_MEMBER(0)
EndStructure

Procedure.i json_object_sort(value.i)
  Protected._JSON_OBJECT *object
  Protected.i size,index
  *object = AllocateStructure(_JSON_OBJECT)
  If *object
    size = JSONObjectSize(value)
    If size
      *object\count = size
      size - 1
      ReDim *object\json(size)
      If ArraySize(*object\json()) = size
        If ExamineJSONMembers(value)
          While NextJSONMember(value)
            *object\json(index)\member = JSONMemberValue(value)
            *object\json(index)\key = JSONMemberKey(value)
            index + 1
          Wend
          SortStructuredArray(*object\json(),#PB_Sort_Ascending,OffsetOf(_JSON_MEMBER\member),#PB_Integer)
          ProcedureReturn *object
        EndIf
      EndIf
    EndIf
    FreeStructure(*object)
  EndIf
  ProcedureReturn #Null
EndProcedure

Procedure.i json_object_free(*object._JSON_OBJECT)
  FreeStructure(*object)
  ProcedureReturn #Null
EndProcedure

Procedure.i main()
  Protected._JSON_OBJECT *a,*b,*c
  Protected.s j
  Protected.i a,b,c
  j = "{" +#DQUOTE$  +"user" +#DQUOTE$  +": {" +#DQUOTE$  +"Peter" +#DQUOTE$  +": {" +#DQUOTE$  +"age" +#DQUOTE$  +": 33," +#DQUOTE$  +"size" +#DQUOTE$  +": 177," +#DQUOTE$  +"hobby" +#DQUOTE$  +": " +#DQUOTE$  +"sport" +#DQUOTE$  +"}," + 
      #DQUOTE$  +"Tom" +#DQUOTE$  +": {" +#DQUOTE$  +"age" +#DQUOTE$  +": 40," +#DQUOTE$  +"size" +#DQUOTE$  +": 180," +#DQUOTE$  +"hobby" +#DQUOTE$  +": " +#DQUOTE$  +"music" +#DQUOTE$  +"}," + 
      #DQUOTE$  +"Michel" +#DQUOTE$  +": {" +#DQUOTE$  +"age" +#DQUOTE$  +": 39," +#DQUOTE$  +"size" +#DQUOTE$  +": 184," +#DQUOTE$  +"hobby" +#DQUOTE$  +": " +#DQUOTE$  +"sport" +#DQUOTE$  +"}}}"
  ParseJSON(0,j)
  *a = json_object_sort(JSONValue(0))
  If *a
    For a = 0 To *a\count - 1
      Debug *a\json(a)\key
      If JSONType(*a\json(a)\member) = #PB_JSON_Object
        *b = json_object_sort(*a\json(a)\member)
        If *b
          For b = 0 To *b\count - 1
            Debug *b\json(b)\key
            If JSONType(*b\json(b)\member) = #PB_JSON_Object
              *c = json_object_sort(*b\json(b)\member)
              If *c
                For c = 0 To *c\count - 1
                  If JSONType(*c\json(c)\member) = #PB_JSON_String
                    Debug #TAB$ + #TAB$ + *c\json(c)\key + " = " + GetJSONString(*c\json(c)\member)  
                  Else
                    Debug #TAB$ + #TAB$ + *c\json(c)\key + " = " + GetJSONInteger(*c\json(c)\member)
                  EndIf
                Next 
                json_object_free(*c)
              EndIf
            EndIf
          Next
          json_object_free(*b)
        EndIf
      EndIf
    Next
    json_object_free(*a)
  EndIf
  FreeJSON(0)
  ProcedureReturn #Null  
EndProcedure

End main()
Sergey
User
User
Posts: 53
Joined: Wed Jan 12, 2022 2:41 pm

Re: Order of JSON items

Post by Sergey »

Wolfram wrote: Sat Jul 12, 2025 1:07 am Hello Sergey,

your example does not work in my case.
I want to get the name in the same order as it is written, but I get Tom, Michel, Peter.
I gave you an idea how to order object members list
See Mijikai's exmaple
Mijikai wrote: Sat Jul 12, 2025 8:09 am Interesting, here is what i came up with.
Hello, Mijikai. Yes, that's right!
Thanks for the example, your code is very good

I used this method many years
And if you want to add members the order not be violated, but compose json order will be wrong
Just test... and sorry for my english

replace ParseJSON(0,j) with

Code: Select all

	Protected *object, *object2
	Protected index
	
	If ParseJSON(0,j)
		*object = JSONValue(0)
		If ExamineJSONMembers(*object)
			While NextJSONMember(*object)
				*object2 = JSONMemberValue(*object)
				
				For index = 1 To 5
					AddJSONMember(*object2, "test_" + Str(index))
				Next
			Wend
		EndIf
		
		Debug ComposeJSON(0)
	EndIf
and add member value for comparing at line
Debug *b\json(b)\key + " = " + Str(*b\json(b)\member) + " <-"

ComposeJSON insert 5 members at beginning
Your code shows them at end of members array

{"user":{"test_1":null,"test_2":null,"test_3":null,"test_4":null,"test_5":null,"Tom":{"age":40,"size":180,"hobby":"music"},"Michel":{"age":39,"size":184,"hobby":"sport"},"Peter":{"age":33,"size":177,"hobby":"sport"}}}

user
Peter = 36575800 <-
age = 33
size = 177
hobby = sport
Tom = 36575960 <-
age = 40
size = 180
hobby = music
Michel = 36576120 <-
age = 39
size = 184
hobby = sport
test_1 = 36576280 <-
test_2 = 36576320 <-
test_3 = 36576360 <-
test_4 = 36584640 <-
test_5 = 36584680 <-


Wonderful! 8)
Post Reply