Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"How to fix `remove default alphabetical ordering of SerializeJSON() `

I'm trying to add the serialized data in a request to third party API which needs a specific order of the data to be maintained, but SerializeJSON orders in alphabetical order which breaks the format required by the third party API. Could someone help me to figure it out

INPUT:

<cfset data ={
                "Booking": {
                    "ActionCode":"DI",
                    "AgencyNumber":"23",
                    "Touroperator":"TVR",
                    "BookingNumber":"323",
                },
                "Payment": {
                    "__type":"paymenttype",
                    "PaymentProfile": {
                        "Value": 4,
                        "Manual": false
                    },
                    "PaymentType": 4,
                    "PaymentAction":2,
                    "Details": {
                        "IBAN": "DE02120300000000202051",
                        "BIC": "BYLADEM1001"
                    }
                },
                "Login":{
                    "UserCode": "usercode",
                    "Password": "password"
                }
            }>

When this method SerializeJSON() is used on my data:

SerializeJSON(data)

Current Output

"{"Booking":{"Touroperator":"TVR","ActionCode":"DI","BookingNumber":"323","AgencyNumber":"23"},"Login":{"UserCode":"usercode","Password":"password"},"Payment":{"PaymentProfile":{"Manual":false,"Value":4},"PaymentType":4,"PaymentAction":2,"__type":"paymenttype","Details":{"BIC":"BYLADEM1001","IBAN":"DE02120300000000202051"}}}"

Expected Output:

"{"Booking":{"ActionCode":"DI","AgencyNumber":"23","Touroperator":"TVR","BookingNumber":"323",},"Payment":{"__type":"paymenttype","PaymentProfile":{"Value":4,"Manual":false},"PaymentType":4,"PaymentAction":2,"Details":{"IBAN":"DE02120300000000202051","BIC":"BYLADEM1001"}},"Login":{"UserCode":"usercode","Password":"password"}}"
like image 378
Kigan Khadka Avatar asked Apr 18 '19 03:04

Kigan Khadka


1 Answers

Structs in ColdFusion are unordered HashMaps, so there is no order at all. You can keep insertion order by using structNew("Ordered") (introduced with ColdFusion 2016). Unfortunately you can no longer use the literal syntax anymore, but I assume you are generating the data dynamically anyway.

<cfset data = structNew("Ordered")>

<cfset data["Booking"] = structNew("Ordered")>
<cfset data["Booking"]["ActionCode"] = "DI">
<cfset data["Booking"]["AgencyNumber"] = "TVR">
<cfset data["Booking"]["BookingNumber"] = "323">

<cfset data["Payment"] = structNew("Ordered")>
<cfset data["Payment"]["__type"] = "paymenttype">
<cfset data["Payment"]["PaymentProfile"] = structNew("Ordered")>
<cfset data["Payment"]["PaymentProfile"]["Value"] = 4>
<cfset data["Payment"]["PaymentProfile"]["Manual"] = false>

etc.

If you are stuck on an older ColdFusion version, you will have to use Java's LinkedHashMap.

<cfset data = createObject("java", "java.util.LinkedHashMap")>

<cfset data["Booking"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Booking"]["ActionCode"] = "DI">
<cfset data["Booking"]["AgencyNumber"] = "TVR">
<cfset data["Booking"]["BookingNumber"] = "323">

<cfset data["Payment"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Payment"]["__type"] = "paymenttype">
<cfset data["Payment"]["PaymentProfile"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Payment"]["PaymentProfile"]["Value"] = 4>
<cfset data["Payment"]["PaymentProfile"]["Manual"] = false>

etc.

But be aware: LinkedHashMap is case-sensitive (and also type-sensitive: in case your keys are numbers, it does matter!).

<cfset data = createObject("java", "java.util.LinkedHashMap")>

<cfset data["Test"] = "">
<!---
    accessing data["Test"] = works
    accessing data["test"] = doesn't work
    accessing data.Test    = doesn't work
--->

Another issue you might encounter: Due to ColdFusion's internal type casting, serializeJSON() might stringify numbers and booleans in an unintended way. Something like:

<cfset data = structNew("Ordered")>
<cfset data["myBoolean"] = true>
<cfset data["myInteger"] = 123>

could easily end up like:

{
    "myBoolean": "YES",
    "myInteger": 123.0
}

(Note: The above literal syntax would work perefectly fine, but if you are passing the values around as variables/arguments, casting eventually happens.)

The easiest workaround is explicitly casting the value before serializing:

<cfset data = structNew("Ordered")>
<cfset data["myBoolean"] = javaCast("boolean", true)>
<cfset data["myInteger"] = javaCast("int", 123)>
like image 105
Alex Avatar answered Oct 16 '22 19:10

Alex