所以这是非常骇人听闻的,可能有几个示例可以按原样破坏它,但如果您绝对无法修复源数据输出或简单地将错误的 JSON 标记为手动调整,这可能是您需要的路线采取并进一步充实。
根据您的示例和我提供的一些附加功能,借助维护排序顺序的自定义字符串拆分表值函数,您可以实现如下输出:
查询
declare @t table (JsonData nvarchar(max));
insert into @t values('{"Eligible":true,"Reason":"","Notes":"Left message for employee to "call me"","EDate":"08/16/2021","Test": "999","Another Test":"Value with " character"}');
with q as
(
select t.JsonData
,s.rn
,case when right(trim(lag(s.item,1) over (order by s.rn)),1) in('{',':',',')
then '"'
else ''
end -- Do we need a starting double quote?
+ s.item -- Value from the split text
+ case when right(trim(lead(s.item,1) over (order by s.rn)),1) not in('}',':',',')
and right(trim(s.item),1) not in('{','}',':',',')
then '\"'
else ''
end -- Do we need an escaped double quote?
+ case when left(trim(lead(s.item,1) over (order by s.rn)),1) in('}',':',',')
then '"'
else ''
end -- Do we need an ending double quote?
as Quoted
from @t as t
cross apply dbo.fn_StringSplit4k(t.JsonData,'"',null) as s -- By splitting on " characters, we know where they all are even though they are removed, so we can add them back in as required based on the remaining text
)
,j as
(
select JsonData
,string_agg(Quoted,'') within group (order by rn) as JsonFixed
from q
group by JsonData
)
select json_value(JsonFixed, '$.Eligible') as Eligible
,json_value(JsonFixed, '$.Reason') as Reason
,json_value(JsonFixed, '$.Notes') as Notes
,json_value(JsonFixed, '$.EDate') as EDate
,json_value(JsonFixed, '$.Test') as Test
,json_value(JsonFixed, '$."Another Test"') as AnotherTest
from j;
输出
| Eligible |
Reason |
Notes |
EDate |
Test |
AnotherTest |
| true |
|
Left message for employee to "call me" |
08/16/2021 |
999 |
Value with " character |
字符串拆分器
create function [dbo].[fn_StringSplit4k]
(
@str nvarchar(4000) = ' ' -- String to split.
,@delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,@num as int = null -- Which value to return.
)
returns table
as
return
-- Start tally table with 10 rows.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in @str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest @str length.
,t(t) as (select top (select len(isnull(@str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(@str,''),t,1) = @delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(@delimiter,isnull(@str,''),s),0)-s,4000) from s)
select rn
,item
from(select row_number() over(order by s) as rn
,substring(@str,s,l) as item
from l
) a
where rn = @num
or @num is null;