Search code examples
javascriptjsoncoldfusionlucee

Why is my JSON invalid even though it looks correct?


I've been working on this for quite a while, and I just don't understand why my JSON is invalid...

JSONLint is showing this error

    Error: Parse error on line 107:
...pair?",      "answer": "Yes, as long as the
----------------------^
Expecting 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '[', got 'undefined'

This is the fragment of the JSON

{
    "tags": "already transferred",
    "question": "Can we transfer customers who have already been transferred previously? what is the dispo? warm transfer or already contacted?",
    "answer": "Yes, mark as already contacted."
},

{
    "tags": "secured debt",
    "question": "If customer only has secured debts, can we still offer credit repair?",
    "answer": "Yes, as long as they have at least $100 in secured/unsecured debt.
    "},




    {
        "tags": "state",
        "question": "Is the program state sensitive?",
        "answer": "Yes, each partner has particular states that they service. The script engine will only offer services when the state is valid for partner who has that service."
    },

It's failing where it says 'Yes, as long'

JSON is created in ColdFusion dynamically.

<cfscript>faqCounter=1;</cfscript>
    <CFLOOP query="getFAQs">
         <cfoutput>
            {"tags":"#getFAQs.tags#","question":"#getFAQs.question#","answer":"#getFAQs.answer#"}<cfif faqCounter<getFAQCount.getFAQPertinentCount>,</cfif>
         </cfoutput>
        <cfscript>faqCounter++;</cfscript>
    </CFLOOP>

Solution

  • ( As the other answers already pointed out, the problem is the un-escaped new line, which breaks the JSON. That's one of the reasons to avoid DIY JSON. Instead, use the built in function SerializeJSON(). )

    Lucee 5.2.8.39+

    Try the new support for JSON serialization-related settings in the Application.cfc. The new settings let you override the bizarre default CF has for serializing query objects:

    // serialize queries as an array of structures AND
    // preserves the column name case used in the sql
    this.serialization.preserveCaseForStructKey = true;
    this.serialization.serializeQueryAs = "struct";
    

    Now you can skip all the query looping. Simply execute the query and call serializeJSON( yourQuery ), to generate a wonderfully sane looking string like this:

    [
      {
        "answer": "Yes, mark as already contacted.",
        "tags": "already transferred",
        "question": "Can we transfer customers who have already been transferred previously? what is the dispo? warm transfer or already contacted?"
      },
      {
        "answer": "Yes, as long as they have at least $100 in secured/unsecured debt.  ",
        "tags": "secured debt",
        "question": "If customer only has secured debts, can we still offer credit repair?"
      }
    ]
    

    Earlier Lucee versions

    For earlier versions, do what @Barmar recommended. Build an array of structures. Then use serializeJSON to convert the array into a properly formatted JSON string.

    Runnable Example

       <cfset yourArray = []>
    
       <cfloop query="getFAQs">
          <cfset yourArray.append( { "tags" : getFAQs.tags
                                   , "question" : getFAQs.question
                                   , "answer": getFAQs.answer
                                 } )>    
       </cfloop>
    
       <cfset jsonString = serializeJSON( yourArray )>
    

    How to remove the new line?

    After generating a "proper" JSON string, run a replace() and substitute \n with an empty string.

      <cfset jsonString  = replace(jsonString , "\n", "", "all")>
    

    To permanently remove them, you'll have to find the code inserting them into the database in the first place, and modify it there. Also, update any existing database records to remove the "\n".