First of all, thank you @BVernon - your answer got me on right track.
I have improved version of this - it is capable of:
- returning strings, numbers or null and handle
- handles JSON quote escaping
- a bit better/robust finding - in case key is part of some other string value, it searches by
"key":
, not just by key
.
CREATE FUNCTION [dbo].[GetJsonValue](@key varchar(100), @data nvarchar(max))
RETURNS nvarchar(max)
AS
BEGIN
DECLARE @keyJson varchar(105) = '"' + @key+ '":'
DECLARE @keyIdx int = CHARINDEX(@keyJson, @data)
IF @keyIdx = 0 RETURN null
DECLARE @valueIdx int = @keyIdx + LEN(@keyJson)
DECLARE @termIdx int = CHARINDEX('"', @data, @valueIdx)
IF @termIdx <> 0 BEGIN
SET @valueIdx = @valueIdx + 1
SET @termIdx = CHARINDEX('"', @data, @valueIdx)
-- Overcome JSON qoute escape
WHILE SUBSTRING(@data, @termIdx-1, 1) = '\'
BEGIN
SET @termIdx = CHARINDEX('"', @data, @termIdx + 1)
END
END ELSE BEGIN
SET @termIdx = CHARINDEX(',', @data, @valueIdx)
IF @termIdx = 0 SET @termIdx = CHARINDEX('}', @data, @valueIdx)
END
IF @termIdx = 0 RETURN null
-- Replace escapte quote before return value
RETURN REPLACE(SUBSTRING(@data, @valueIdx, @termIdx - @valueIdx), '\"', '"')
END
It still have limitations:
- handling nested objects is not supported, moreover it could give false results, if the key is somewhere deep in object
- does not support anything but returning scalar value by key
These are some tests:
-- These work just fine
print [dbo].[GetJsonValue]('foo', '{"foo":"bar"}')
print [dbo].[GetJsonValue]('foo', '{"foo":"Quoted \"bar\""}')
print [dbo].[GetJsonValue]('foo', '{"foo":55}')
print [dbo].[GetJsonValue]('foo', '{"foo":null}')
print [dbo].[GetJsonValue]('foo', '{"a":"foo","foo":"baz"}') -- no false positive
-- CANNOT HANDLE SPACES
print [dbo].[GetJsonValue]('foo', '{"foo": "bar"}')
-- FALSE POSITIVE!!!
print [dbo].[GetJsonValue]('foo', '{"nested:{"foo":123}}')
print [dbo].[GetJsonValue]('foo', '[{"foo":123}]')
Output is:
bar
Quoted "bar"
55
null
baz
123
123