sql-serveropen-json

How to work out the path for all values in a JSON


I have a JSON column that has about 300k worth of properties, and I want to attempt to export it in such a way that I can get the full path and value of the property. Note, my JSON has no arrays - it's basically a JSON for a web application translations.

To provide a simple example with the following JSON I want to get back the following:

JSON: {"Name":"Jeff", "Address": {"Street": "123 Harrow Street", "City": "Dublin"}}

Expected Output:

|----------------|-------------------|
| path           |  value            |
|----------------|-------------------|
| Name           | Jeff              |
| Address.Street | 123 Harrow Street |
| Address.City   | Dublin            |
|----------------|-------------------|

Or if it's easier I can do with the $. because I'd like to be able to easily update the values for each property - with JSON_MODIFY.

I've tried using OPENJSON but that only appears to return 3 fields key, value and type, but at least the key here doesn't go past the field level of values, so I get: Query:

DECLARE @json_doc nvarchar(4000) = '{"Name":"Jeff", "Address": {"Street": "123 Harrow Street", "City": "Dublin"}}';

SELECT [key], [value]
FROM OPENJSON(@json_doc);
GO

Output:

|---------|---------------------------------------------------|
| key     |  value                                            |
|---------|---------------------------------------------------|
| Name    | Jeff                                              |
| Address | {"Street": "123 Harrow Street", "City": "Dublin"} |
|---------|---------------------------------------------------|

Is there a way to get the OPENJSON query to be fully recursive? Or is there another way? I've tried googling but it doesn't appear to be a common request?


Solution

  • A recursive CTE is an option. You need to use ISJSON() as a termination condition in the recursive member of the query:

    DECLARE @json_doc nvarchar(4000) = '{"Name":"Jeff", "Address": {"Street": "123 Harrow Street", "City": "Dublin"}}';
    
    ;WITH rCTE AS (
       SELECT 
           CONVERT(nvarchar(max), N'$') COLLATE DATABASE_DEFAULT AS [path], 
           CONVERT(nvarchar(max), JSON_QUERY(@json_doc, '$')) COLLATE DATABASE_DEFAULT AS [value]
       UNION ALL
       SELECT 
          CONVERT(nvarchar(max), CONCAT(r.path, CONCAT(N'.', c.[key]))) COLLATE DATABASE_DEFAULT ,
          CONVERT(nvarchar(max), c.[value]) COLLATE DATABASE_DEFAULT                                        
       FROM rCTE r
       CROSS APPLY OPENJSON(r.[value]) c
       WHERE ISJSON(r.[value]) = 1
    )
    SELECT *
    FROM rCTE
    WHERE ISJSON([value]) = 0
    

    Result:

    path             value
    ----------------------------------
    $.Name           Jeff
    $.Address.Street 123 Harrow Street
    $.Address.City   Dublin