I think there isn't a normal way to do it.
I've found only the following way how to do it
-- create and fill #TestData SELECT '["qwe","asd","123"]' json_str INTO #TestData UNION ALL SELECT '["zxc","asd","321"]' UNION ALL SELECT '[]' UNION ALL SELECT '["qwe","asd","12\"3"]' -- escaped quotes UNION ALL SELECT '["qwe","asd","<123>"]' -- special characters -- delete demo SELECT json_str, JSON_QUERY(CONCAT('[',STUFF(( SELECT CONCAT(',"',REPLACE([value],'"','\"'),'"') FROM OPENJSON(json_str, '$') WHERE [key]<>1 -- exclude element with index 1 ORDER BY [key] FOR XML PATH('')),1,1,''),']')) new_json_str FROM #TestData -- insert demo SELECT json_str, JSON_QUERY(CONCAT('[',STUFF(( SELECT CONCAT(',"',REPLACE([value],'"','\"'),'"') FROM ( SELECT 1 RowPriority,[key],[value] FROM OPENJSON(json_str, '$') UNION ALL SELECT 2 RowPriority, 0, -- add after key with index 0 'new value' -- value ) q ORDER BY [key],RowPriority -- sort values FOR XML PATH('')),1,1,''),']')) new_json_str FROM #TestData DROP TABLE #TestData
And one more example to delete values by another condition and to insert several values
-- create and fill #TestData SELECT '["qwe","asd","123"]' json_str INTO #TestData UNION ALL SELECT '["zxc","asd","321"]' UNION ALL SELECT '[]' -- empty array UNION ALL SELECT '["qwe","asd","12\"3"]' -- escaped quotes UNION ALL SELECT '["qwe","asd","<123>"]' -- special characters -- delete with another condition demo SELECT json_str, JSON_QUERY(CONCAT('[',STUFF(( SELECT CONCAT(',"',REPLACE([value],'"','\"'),'"') FROM OPENJSON(json_str, '$') WHERE [value] NOT IN('qwe','123') -- another delete condition ORDER BY [key] FOR XML PATH('')),1,1,''),']')) new_json_str FROM #TestData -- insert several values demo SELECT json_str, JSON_QUERY(CONCAT('[',STUFF(( SELECT CONCAT(',"',REPLACE([value],'"','\"'),'"') FROM ( SELECT 1 RowPriority,0 NewValOrder,[key],[value] FROM OPENJSON(json_str, '$') UNION ALL SELECT 2 RowPriority,NewValOrder,AfterPosition,[value] FROM (VALUES(0,1,'new value 1'),(0,2,'new value 2')) v(AfterPosition,NewValOrder,[value]) ) q ORDER BY [key],RowPriority,NewValOrder -- sort values FOR XML PATH('')),1,1,''),']')) new_json_str FROM #TestData DROP TABLE #TestData
Hope this variant will suit you.
A variant with STRING_AGG instead FOR XML PATH('')
-- create and fill #TestData SELECT '["qwe","asd","123"]' json_str INTO #TestData UNION ALL SELECT '["zxc","asd","321"]' UNION ALL SELECT '[]' -- empty array UNION ALL SELECT '["qwe","asd","12\"3"]' -- escaped quotes UNION ALL SELECT '["qwe","asd","<123>"]' -- special characters -- delete with another condition demo SELECT json_str, JSON_QUERY(CONCAT('[', ( SELECT STRING_AGG(CONCAT('"',REPLACE([value],'"','\"'),'"'),',') WITHIN GROUP(ORDER BY [key]) FROM OPENJSON(json_str, '$') WHERE [value] NOT IN('qwe','123') -- another delete condition ),']')) new_json_str FROM #TestData -- insert several values demo SELECT json_str, JSON_QUERY(CONCAT('[', ( SELECT STRING_AGG(CONCAT('"',REPLACE([value],'"','\"'),'"'),',') WITHIN GROUP(ORDER BY [key],RowPriority,NewValOrder) FROM ( SELECT 1 RowPriority,0 NewValOrder,[key],[value] FROM OPENJSON(json_str, '$') UNION ALL SELECT 2 RowPriority,NewValOrder,AfterPosition,[value] FROM (VALUES(0,1,'new value 1'),(0,2,'new value 2')) v(AfterPosition,NewValOrder,[value]) ) q ),']')) new_json_str FROM #TestData DROP TABLE #TestData
Maybe the last variant will be better because construction FOR XML PATH('') replaces some special characters (for example: < - <).