19

I receive the following JSON string from an API function.

"Inbound": {
    "callRelatedFields": ["ANI",
    "DNIS"],
    "objects": {
        "Contact": [{
            "displayName": "Name",
            "apiName": "Name"
        },
        {
            "displayName": "Email",
            "apiName": "Email"
        }],
        "Account": [{
            "displayName": "Account Name",
            "apiName": "Name"
        },
        {
            "displayName": "Phone",
            "apiName": "Phone"
        },
        {
            "displayName": "Fax",
            "apiName": "Fax"
        }],
        "cnx__Phone__c": [{
            "displayName": "Phone Name",
            "apiName": "Name"
        },
        {
            "displayName": "Phone Number Line 1",
            "apiName": "cnx__Phone_Number_Line_1__c"
        },
        {
            "displayName": "Phone Number Line 2",
            "apiName": "cnx__Phone_Number_Line_2__c"
        },
        {
            "displayName": "Type",
            "apiName": "cnx__Type__c"
        },
        {
            "displayName": "Location",
            "apiName": "cnx__Location__c"
        },
        {
            "displayName": "Call Manager",
            "apiName": "cnx__Call_Manager__c"
        },
        {
            "displayName": "Mac Address",
            "apiName": "cnx__Mac_Address__c"
        }]
    },
    "screenPopSettings": {
        "screenPopsOpenWithin": "ExistingWindow",
        "SingleMatch": {
            "screenPopType": "PopToEntity"
        },
        "NoMatch": {
            "screenPopType": "DoNotPop"
        },
        "MultipleMatches": {
            "screenPopType": "DoNotPop"
        }
    }
}

The order of the objects inside "objects" is important! But when i parse this JSON string with JSON.parse, the order of those objects is lost.

Is there any good way to keep the order of those objects after they are parsed.

I tried to manipulate the string and convert the whole "objects" into an array, but this turned out to become way too complicated and hacky.

Praind
  • 1,551
  • 1
  • 12
  • 25
  • 1
    "An object is an unordered set of name/value pairs" by definition. If the order of your `objects` is important, then they should be in an array. – icke Aug 14 '15 at 09:07
  • I know but i can't change the JSON format since this is the way i receive the data from this API. The order in this JSON string is always correct but you're right... Since it is an object it is not meant to be ordered. – Praind Aug 14 '15 at 09:14
  • @JonathanCamenzind How are you determining that the order of keys in the object has changed? If you are logging it to the Chrome devtools console, I noticed today that it shows you the object with keys in alphabetical order. The object itself probably doesn't have the keys in that order, though. What does logging `Object.keys(JSON.parse(str))` show? – GregL Aug 14 '15 at 10:12
  • I posted an answer of this issue here. https://stackoverflow.com/a/65817509/11512694 – Aladin Mrebai Jan 20 '21 at 21:03
  • @AladinMrebai I didn't see a post by you at that link. How did you solve this? I think it's strange that the JavaScript language would mess with the order. I understand having options to do things different ways but there should always be an option to preserve the order that the items appear it in the input JSON array. I still haven't found a clean solution for this. – iPaul Jun 08 '22 at 19:07

6 Answers6

17

I have a suspicion that the thing that makes you think the keys have changed order is that Chrome devtools show objects with their keys sorted in alphabetical order. Whereas if you use Object.keys() or the equivalent JS to manually iterate through the keys, you will find they come out in the order they were defined in the JSON string.

Screenshot from Chrome devtools

Here is the equivalent JS for Object.keys():

function objectKeys(obj) {
    var keys = [];
    if (!obj) return keys;
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            keys.push(key);
        }
    }
}

When I call this with the objects part of the parsed object I get the following array:

["Contact", "Account", "cnx__Phone__c"]
David Roussel
  • 5,788
  • 1
  • 30
  • 35
GregL
  • 37,147
  • 8
  • 62
  • 67
  • 2
    This was true in my case. Doing a `console.log( myObj );` listed everything in alphabetical order. I'm using jQuery and doing `$.each( myObj, function( key, value ) { console.log( key + ': ' + value ); } );` outputs everything in the correct order. – Gavin Oct 01 '20 at 05:50
  • 2
    The key order is not respected for numeric stringified keys!! Object.keys({"2":1,"1":1}). returns [1,2] – jacmkno Dec 31 '21 at 21:58
7

Unfortunately object properties are unordered in JavaScript so you shouldn't rely on being able to iterate over them in a particular sequence.

I would suggest accessing the properties by name in the order you need them, rather than just iterating over the list.

codebox
  • 19,927
  • 9
  • 63
  • 81
  • 2
    Is there a browser that re-orders the properties from what they are declared in the JSON? It might not be part of the official standard but it might be a defacto standard. Chromium and Firefox keep the order. – RandomEngy Mar 19 '21 at 21:29
2

As per the JSON standard, an object is unordered. So if you care about the order "Contact", "Account", "cnx__Phone__c", put them in an array ([]).

Maybe it's enough to put the property names themselves in an array next to the .objects themselves, so that you still can access them by their names. Many structures are valid solutions.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • But how would you do that? Would you change the format with string manipulation? Because i can't change how the data is sent since this string comes from an external API function :( – Praind Aug 14 '15 at 09:30
  • 1
    Uh, you should really make them fix their API. In the meantime, any JS code should work, [as browser use the order of property definition in enumerations](http://stackoverflow.com/a/280861/1048572) – Bergi Aug 14 '15 at 09:36
1

This solution works only if the properties and the data does not contain one of these characters: {, } and :.

Maybe you replace the curly brackets to square brackets and ": to #",. After that, you can the JSON string parse and get all objects replaced by arrays. The reading is: first value is the property (marked with # at the end) and the second value is the value.

The replacement machanism shuld be improved, in particular the replacement of ":, which can sometimes be wrong, and the search of the curly brackets.

var json = '{"Inbound":{"callRelatedFields":["ANI","DNIS"],"objects":{"Contact":[{"displayName":"Name","apiName":"Name"},{"displayName":"Email","apiName":"Email"}],"Account":[{"displayName":"Account Name","apiName":"Name"},{"displayName":"Phone","apiName":"Phone"},{"displayName":"Fax","apiName":"Fax"}],"cnx__Phone__c":[{"displayName":"Phone Name","apiName":"Name"},{"displayName":"Phone Number Line 1","apiName":"cnx__Phone_Number_Line_1__c"},{"displayName":"Phone Number Line 2","apiName":"cnx__Phone_Number_Line_2__c"},{"displayName":"Type","apiName":"cnx__Type__c"},{"displayName":"Location","apiName":"cnx__Location__c"},{"displayName":"Call Manager","apiName":"cnx__Call_Manager__c"},{"displayName":"Mac Address","apiName":"cnx__Mac_Address__c"}]},"screenPopSettings":{"screenPopsOpenWithin":"ExistingWindow","SingleMatch":{"screenPopType":"PopToEntity"},"NoMatch":{"screenPopType":"DoNotPop"},"MultipleMatches":{"screenPopType":"DoNotPop"}}}}';
json = json.replace(/{/g, '[').replace(/}/g, ']').replace(/"\:/g, '#",');
json = JSON.parse(json);
document.write('<pre>' + JSON.stringify(json, 0, 4) + '</pre>');
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

@GregL is right the JSON parsed came in alphabetic or in case of a number in ascending order and to keep the order you'll need an incremented number logic like:

var position_in_array = 0
var name = 'screenPopSettings'

object[`${position_in_array}${name}`] = value

position_in_array += 1
Valter
  • 26
  • 1
  • 7
0

The parseJson returns data in object form and object doesn't has index. So we should define custom index of data array, if we want to keep the array index.

Example:

$arr[0] = array(
'Contact'=>array(
'key1'=>'val',
)
); 
$arr[1] = array(
'Account'=>array(
'key1'=>'val',
)
); 

It will produce the output as per the array index originally defined before parseJson function call.

Reema Parakh
  • 1,347
  • 3
  • 19
  • 46