79812864

Date: 2025-11-07 23:26:10
Score: 1
Natty:
Report link

Little late here, but this will solve the problem of converting newlines
in Value strings (only) either in arrays or Objects, to a literal \n

It can be tailored to to either or both types of values.
If the same needs to be done to keys, contact me for that mod.

The core JSON regex functions are done by me @sln.
More examples and explanations can be found here :
https://stackoverflow.com/a/79785886/15577665

This is converted to Ruby using Oniguruma recursion call syntax \g<name>
and a dead cluster to house the functions (?:(?<func1>..)(?<func2>..){0}, etc...

The code flows as follows -
Step1: Validate the JSON string. Uses RxValidate regex below.
Step2: If the JSON passes the validation regex, match the quoted strings that have newlines embedded.
Step3: When the string is matched, in a callback, replace all newlines CRLF in the quoted string with the literal \n

Important Note >> Because the string has been Validated, we know that all that's needed
to match the quoted values is to impose a Separator check at the end of the quote part of the regex.
This is because there is no other form where quoted values can exist in valid JSON.

This regex validates the JSON The string can only contain a Valid JSON.

\A \s*    
(?: \g<V_Obj> | \g<V_Ary> )
\s* \z    

# JSON functions - NoErDet
# ---------------------------------------------
(?:(?<Sep_Ary>\s*(?:,(?!\s*[}\]])|(?=\])))(?<Sep_Obj>\s*(?:,(?!\s*[}\]])|(?=})))(?<Str>(?>"[^\\"]*(?:\\[\s\S][^\\"]*)*"))(?<Numb>(?>[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|(?:[eE][+-]?\d+)))(?<V_KeyVal>(?>\s*\g<Str>\s*:\s*\g<V_Value>\s*))(?<V_Value>(?>\g<Numb>|(?>true|false|null)|\g<Str>|\g<V_Obj>|\g<V_Ary>))(?<V_Ary>\[(?>\s*\g<V_Value>\g<Sep_Ary>)*\s*\])(?<V_Obj>{(?>\g<V_KeyVal>\g<Sep_Obj>)*\s*})){0}

This regex matches the quoted value strings containing embedded newlines.

"                             # The "Value" with at least one newline in it
[^\\"\r\n]*? 
\r? \n [^\\"]* 
(?: \\ [\s\S] [^\\"]* )*
"
(?=                           # Assert : Ahead an object or array separator
   \s* 
   (?: \g<Sep_Obj> | \g<Sep_Ary> )
)
# JSON Separators
# -------------------
(?:
   (?<Sep_Ary>     
      \s* 
      (?:
         ,
         (?! \s* [}\]] )
       | (?= \] )
      )
   )               
   (?<Sep_Obj>     
      \s* 
      (?:
         ,
         (?! \s* [}\]] )
       | (?= } )
      )
   )               
){0}

Ruby Test Code The JSON sample data and output are separately at the bottom.

RxValidate = /\A\s*(?:\g<V_Obj>|\g<V_Ary>)\s*\z(?:(?<Sep_Ary>\s*(?:,(?!\s*[}\]])|(?=\])))(?<Sep_Obj>\s*(?:,(?!\s*[}\]])|(?=})))(?<Str>(?>"[^\\"]*(?:\\[\s\S][^\\"]*)*"))(?<Numb>(?>[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|(?:[eE][+-]?\d+)))(?<V_KeyVal>(?>\s*\g<Str>\s*:\s*\g<V_Value>\s*))(?<V_Value>(?>\g<Numb>|(?>true|false|null)|\g<Str>|\g<V_Obj>|\g<V_Ary>))(?<V_Ary>\[(?>\s*\g<V_Value>\g<Sep_Ary>)*\s*\])(?<V_Obj>{(?>\g<V_KeyVal>\g<Sep_Obj>)*\s*})){0}/
RxQtNL = /"[^\\"\r\n]*?\r?\n[^\\"]*(?:\\[\s\S][^\\"]*)*"(?=\s*(?:\g<Sep_Obj>|\g<Sep_Ary>))(?:(?<Sep_Ary>\s*(?:,(?!\s*[}\]])|(?=\])))(?<Sep_Obj>\s*(?:,(?!\s*[}\]])|(?=})))){0}/

count = 0   # count on newline replacements made

## If valid json string, replace newlines into stringed  '\n'
##
if json =~ RxValidate
   puts "Valid JSON"
   ## Here, Match only Value strings with newline(s) in it
   json_new = json.gsub( RxQtNL ) {
          ## Callback1:  Matched a quoted string with newline(s)
         |m| m.gsub( /\r?\n/) {
               ## Callback2 (just to increment counter): 
               |m| count+=1;  # Increment counter
               "\\n"          # Return replacement
            }
      }
   puts json_new
   puts "Changed " + count.to_s + " newlines into literal  \\n"
else
   puts "Invalid JSON "
end

Coded JSON Test sample

json = '
{
    "calculation":"
setCreate(formData,
  path(\'hifSpendSinceLastReturn\', \'hifSpendSinceLastReturnHolder\', \'remaining\'),
  String(
    (parseMoney(
      (get(formData, path(\'fundedThroughHIF\',\'fundedThroughHIFCurrent\')) === \'Yes\') ?
        get(formData, path(\'totalCost\', \'previousAmounts\', \'baseline\')) :
        get(formData, path(\'fundedThroughHIF\', \'hifSpend\', \'previousAmounts\', \'baseline\')
      )
    ) -
    parseMoney(
      get(formData, path(\'hifSpendSinceLastReturn\', \'hifSpendSinceLastReturnHolder\', \'cumulativeIncCurrentReturn\')))
    )
  )
);

get(formData, \'anyChangeToDescription\', \'confirmation\') === \'Yes\' ?
  set(formData, \'currentFundingStackDescription\', get(formData, path(\'anyChangeToDescription\', \'updatedFundingStack\'))) :
  set(formData, \'currentFundingStackDescription\', get(formData, path(\'descriptionOfFundingStack\'))); set(formData[\'totalCost\'], \'percentComplete\', calculateVariance(parseMoney(get(formData, path(\'totalCost\', \'current\'))), parseMoney(get(formData, path(\'totalCost\', \'baseline\')))));"
    ,

    "calculation": "
get(formData, \'anyChange\') === \'Yes\' ?
  setCreate(formData, path(\'variance\', \'currentAmount\'), get(formData, path(\'variance\', \'current\'))) :
  setCreate(formData, path(\'variance\', \'currentAmount\'), get(formData, path(\'previousAmounts\', \'lastReturn\')));

setCreate(formData, path(\'variance\', \'baseline\'),
  String(parseMoney(get(formData, \'variance\', \'current\')) - parseMoney(get(formData, path(\'previousAmounts\', \'baseline\'))))
);

get(formData,\'previousAmounts\', \'lastReturn\') ?
  setCreate(formData, path(\'variance\', \'lastReturn\'),
    String(parseMoney(get(formData, \'variance\', \'current\')) -
      parseMoney(get(formData, path(\'previousAmounts\', \'lastReturn\'))))
  ) :
  setCreate(formData, path(\'variance\', \'lastReturn\'), \'N/A\');"
    ,
    "calculation": "
get(formData, \'anyChange\', \'confirmation\') === \'Yes\' ?
  get(formData, \'fundedThroughHIFbaseline\') === \'Yes\' ?
    set(formData, \'fundedThroughHIFCurrent\', \'No\') :
    set(formData, \'fundedThroughHIFCurrent\', \'Yes\')
    :
  set(formData, \'fundedThroughHIFCurrent\', get(formData, \'fundedThroughHIFbaseline\'));"
    ,

    "hifSpend": {
       "title": "",
       "calculation": "
get(formData, \'anyChangeToBaseline\', \'confirmation\') === \'Yes\' ?
  setCreate(formData, path(\'anyChangeToBaseline\', \'variance\', \'currentAmount\'), get(formData, \'anyChangeToBaseline\', \'variance\', \'current\')) :
  setCreate(formData, path(\'anyChangeToBaseline\', \'variance\', \'currentAmount\'), get(formData, \'previousAmounts\', \'lastReturn\'));

setCreate(formData, path(\'anyChangeToBaseline\', \'variance\', \'baseline\'),
  String(parseMoney(get(formData,\'anyChangeToBaseline\', \'variance\', \'current\'))) -
  parseMoney(get(formData, \'previousAmounts\', \'baseline\'))
);
get(formData, path(\'previousAmounts\', \'lastReturn\')) ?
  setCreate(formData, path(\'anyChangeToBaseline\', \'variance\', \'lastReturn\'),
    String(parseMoney(get(formData, path(\'anyChangeToBaseline\', \'variance\', \'current\')))
  ) - parseMoney(get(formData, path(\'previousAmounts\', \'lastReturn\')))) :
  setCreate(formData, [\'anyChangeToBaseline\', \'variance\', \'lastReturn\'], \'N/A\');",
        "type": "object",
        "properties": {
            "previousAmounts": {
            "type": "object",
            "title": "HIF Amount",
            "horizontal": true,
            "properties": {
              "baseline": {
                "type": "string",
                "title": "HIF Baseline Amount",
                "sourceKey": [
                   "baseline_data",
                   "costs",
                   "infrastructure",
                   "HIFAmount"
                 ],
                "readonly": true,
                "currency": true
            },
            "lastReturn": {
              "type": "string",
              "title": "Last Return",
              "readonly": true,
              "currency": true,
              "sourceKey": [
                "return_data",
                "fundingPackages",
                "fundingStack",
                "hifSpend",
                "current"
              ]
            }
          }
        },
        "anyChangeToBaseline": {
          "type": "object",
          "title": "",
          "properties": {
            "confirmation": {
              "title": "Any change to baseline/ last return?",
              "type": "string",
              "enum": ["Yes", "No"],
              "radio": true
            }
          },
          "dependencies": {
            "confirmation": {
              "oneOf": [
                {
                  "properties": {
                    "confirmation": {
                      "enum": ["Yes"]
                    },
                    "variance": {
                      "type": "object",
                      "title": "",
                      "horizontal": true,
                      "properties": {
                        "current": {
                          "type": "string",
                          "title": "Current return",
                          "currency": true
                        },
                        "currentAmount": {
                          "type": "string",
                          "title": "",
                          "hidden": true
                      },
                      "baseline": {
                        "type": "string",
                        "title": "Variance against baseline (£)",
                        "readonly": true
                      },
                      "lastReturn": {
                        "type": "string",
                        "title": "Variance Against Last Return (£)",
                        "readonly": true
                      }
                    }
                  },
                  "varianceReason": {
                      "type": "string",
                      "title": "Reason for variance",
                      "extendedText": true
                    }
                  }
                },
                {
                  "properties": {
                    "confirmation": { "enum": ["No"] }
                  }
                }
              ]
           }
         }
       }
     }
   }
}
'

Output

Valid JSON
Changed 51 newlines into literal  `\n`

{
    "calculation":"\nsetCreate(formData,\n  path('hifSpendSinceLastReturn', 'hifSpendSinceLastReturnHolder', 'remaining'),\n  String(\n    (parseMoney(\n      (get(formData, path('fundedThroughHIF','fundedThroughHIFCurrent')) === 'Yes') ?\n        get(formData, path('totalCost', 'previousAmounts', 'baseline')) :\n        get(formData, path('fundedThroughHIF', 'hifSpend', 'previousAmounts', 'baseline')\n      )\n    ) -\n    parseMoney(\n      get(formData, path('hifSpendSinceLastReturn', 'hifSpendSinceLastReturnHolder', 'cumulativeIncCurrentReturn')))\n    )\n  )\n);\n\nget(formData, 'anyChangeToDescription', 'confirmation') === 'Yes' ?\n  set(formData, 'currentFundingStackDescription', get(formData, path('anyChangeToDescription', 'updatedFundingStack'))) :\n  set(formData, 'currentFundingStackDescription', get(formData, path('descriptionOfFundingStack'))); set(formData['totalCost'], 'percentComplete', calculateVariance(parseMoney(get(formData, path('totalCost', 'current'))), parseMoney(get(formData, path('totalCost', 'baseline')))));"
    ,

    "calculation": "\nget(formData, 'anyChange') === 'Yes' ?\n  setCreate(formData, path('variance', 'currentAmount'), get(formData, path('variance', 'current'))) :\n  setCreate(formData, path('variance', 'currentAmount'), get(formData, path('previousAmounts', 'lastReturn')));\n\nsetCreate(formData, path('variance', 'baseline'),\n  String(parseMoney(get(formData, 'variance', 'current')) - parseMoney(get(formData, path('previousAmounts', 'baseline'))))\n);\n\nget(formData,'previousAmounts', 'lastReturn') ?\n  setCreate(formData, path('variance', 'lastReturn'),\n    String(parseMoney(get(formData, 'variance', 'current')) -\n      parseMoney(get(formData, path('previousAmounts', 'lastReturn'))))\n  ) :\n  setCreate(formData, path('variance', 'lastReturn'), 'N/A');"
    ,
    "calculation": "\nget(formData, 'anyChange', 'confirmation') === 'Yes' ?\n  get(formData, 'fundedThroughHIFbaseline') === 'Yes' ?\n    set(formData, 'fundedThroughHIFCurrent', 'No') :\n    set(formData, 'fundedThroughHIFCurrent', 'Yes')\n    :\n  set(formData, 'fundedThroughHIFCurrent', get(formData, 'fundedThroughHIFbaseline'));"
    ,

    "hifSpend": {
       "title": "",
       "calculation": "\nget(formData, 'anyChangeToBaseline', 'confirmation') === 'Yes' ?\n  setCreate(formData, path('anyChangeToBaseline', 'variance', 'currentAmount'), get(formData, 'anyChangeToBaseline', 'variance', 'current')) :\n  setCreate(formData, path('anyChangeToBaseline', 'variance', 'currentAmount'), get(formData, 'previousAmounts', 'lastReturn'));\n\nsetCreate(formData, path('anyChangeToBaseline', 'variance', 'baseline'),\n  String(parseMoney(get(formData,'anyChangeToBaseline', 'variance', 'current'))) -\n  parseMoney(get(formData, 'previousAmounts', 'baseline'))\n);\nget(formData, path('previousAmounts', 'lastReturn')) ?\n  setCreate(formData, path('anyChangeToBaseline', 'variance', 'lastReturn'),\n    String(parseMoney(get(formData, path('anyChangeToBaseline', 'variance', 'current')))\n  ) - parseMoney(get(formData, path('previousAmounts', 'lastReturn')))) :\n  setCreate(formData, ['anyChangeToBaseline', 'variance', 'lastReturn'], 'N/A');",
        "type": "object",
        "properties": {
            "previousAmounts": {
            "type": "object",
            "title": "HIF Amount",
            "horizontal": true,
            "properties": {
              "baseline": {
                "type": "string",
                "title": "HIF Baseline Amount",
                "sourceKey": [
                   "baseline_data",
                   "costs",
                   "infrastructure",
                   "HIFAmount"
                 ],
                "readonly": true,
                "currency": true
            },
            "lastReturn": {
              "type": "string",
              "title": "Last Return",
              "readonly": true,
              "currency": true,
              "sourceKey": [
                "return_data",
                "fundingPackages",
                "fundingStack",
                "hifSpend",
                "current"
              ]
            }
          }
        },
        "anyChangeToBaseline": {
          "type": "object",
          "title": "",
          "properties": {
            "confirmation": {
              "title": "Any change to baseline/ last return?",
              "type": "string",
              "enum": ["Yes", "No"],
              "radio": true
            }
          },
          "dependencies": {
            "confirmation": {
              "oneOf": [
                {
                  "properties": {
                    "confirmation": {
                      "enum": ["Yes"]
                    },
                    "variance": {
                      "type": "object",
                      "title": "",
                      "horizontal": true,
                      "properties": {
                        "current": {
                          "type": "string",
                          "title": "Current return",
                          "currency": true
                        },
                        "currentAmount": {
                          "type": "string",
                          "title": "",
                          "hidden": true
                      },
                      "baseline": {
                        "type": "string",
                        "title": "Variance against baseline (£)",
                        "readonly": true
                      },
                      "lastReturn": {
                        "type": "string",
                        "title": "Variance Against Last Return (£)",
                        "readonly": true
                      }
                    }
                  },
                  "varianceReason": {
                      "type": "string",
                      "title": "Reason for variance",
                      "extendedText": true
                    }
                  }
                },
                {
                  "properties": {
                    "confirmation": { "enum": ["No"] }
                  }
                }
              ]
           }
         }
       }
     }
   }
}
Reasons:
  • Blacklisted phrase (1): stackoverflow
  • Blacklisted phrase (0.5): contact me
  • Contains signature (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @sln
  • High reputation (-1):
Posted by: sln