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: < - <).