Page 1 of 1

Order of JSON items

Posted: Tue Jul 08, 2025 2:45 pm
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

Re: Order of JSON items

Posted: Tue Jul 08, 2025 3:40 pm
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. :-)

Re: Order of JSON items

Posted: Tue Jul 08, 2025 10:14 pm
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!)

Re: Order of JSON items

Posted: Thu Jul 10, 2025 4:38 pm
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.

Re: Order of JSON items

Posted: Thu Jul 10, 2025 7:09 pm
by Sergey
I'm used a pointer to json object in memory (from min to max), maybe this help you order json members :wink:

Re: Order of JSON items

Posted: Fri Jul 11, 2025 10:01 am
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() ?

Re: Order of JSON items

Posted: Fri Jul 11, 2025 5:47 pm
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!

Re: Order of JSON items

Posted: Sat Jul 12, 2025 1:07 am
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

Re: Order of JSON items

Posted: Sat Jul 12, 2025 8:09 am
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()

Re: Order of JSON items

Posted: Sat Jul 12, 2025 10:21 am
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)