Преобразуйте запрос, производящий XML, чтобы он производил JSON. - PullRequest
1 голос
/ 31 мая 2019

У меня есть запрос (см. Ниже), который используется для создания вывода в виде XML.Запрос имеет дело с системой метаданных (EAV), которая имеет несколько типов столбцов и данных.

Мне нужна версия этого запроса, чтобы получить вывод в формате JSON

У меня уже была попытка, но столкнулся с проблемами.

1] Мне нужно ЗАКРЫТЬ данные столбца nvarchar (max) как nvarchar (4000), что не идеально, что-то связанное с конкатенацией, я думаю.

2] данные столбцов XML и JSON неправильно отформатированы, чтобы быть действительными в формате JSON.

Я не пробовал использовать OPENJSON или CTE, но готов что-то предпринять.

У меня есть запросв SQL Server 2017 (см. ниже), который использовался для создания XML-данных, и мне нужно изменить его, чтобы вместо этого создавать результаты в виде JSON.Перепробовал кучу вещей, но не совсем смог получить его.Цени любую помощь.

Уже есть попытка запроса для версии JSON, но у нее есть несколько проблем.1] Мне нужно CAST nvarchar как 4000, иначе он обрезает данные (не идеально).2] Не удается получить столбцы типа xml и json для получения правильного вывода.

Не пробовал использовать OPENJSON или CTE, но открыт для идей.

-- structure
    DECLARE @metaFields TABLE(
        [metaFieldID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
        [metasetID] [uniqueidentifier] NOT NULL,
        [metaColumnID] [uniqueidentifier] NOT NULL,
        [systemRef] [varchar](255) NOT NULL,
        [displayType] [varchar](50) NOT NULL
    )


    DECLARE @metaColumns TABLE (
        [metaColumnID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
        [storageTable] [varchar](255) NOT NULL,
        [storageColumn] [varchar](255) NOT NULL,
        [storageType] [varchar](10) NOT NULL,
        [storageSize] [smallint] NULL
    )


    DECLARE @metaDataObjectVersions TABLE (
            [metaDataObjectVersionID] [int] NOT NULL,
            [xmlVersionData] [xml] NULL
    )


    DECLARE  @metaData TABLE (
        [metaDataID] [int] NOT NULL,
        [metaDataObjectVersionID] [int] NOT NULL,
        [metaColumnID] [uniqueidentifier] NOT NULL,
        [numericData] [real] NULL,
        [dateData] [datetime] NULL,
        [textData] [nvarchar](max) NULL,
        [GUIDData] [uniqueidentifier] NULL,
        [xmlData] [xml] NULL,
        [jsonData] [nvarchar](max) NULL
    )

    -- data
    INSERT INTO @metaFields
        ([metaFieldID],[metasetID],[metaColumnID],[systemRef],[displayType])
    VALUES
        ('FFFFFFFF-0000-0000-0000-000000000001','AAAAAAAA-0000-0000-0000-000000000001','CCCCCCCC-0000-0000-0000-000000000001','ACTIVE','RADIOBUTTON'),
        ('FFFFFFFF-0000-0000-0000-000000000002','AAAAAAAA-0000-0000-0000-000000000001','CCCCCCCC-0000-0000-0000-000000000002','CREATED','TEXT'),
        ('FFFFFFFF-0000-0000-0000-000000000003','AAAAAAAA-0000-0000-0000-000000000001','CCCCCCCC-0000-0000-0000-000000000003','CONTENT','TEXTAREA'),
        ('FFFFFFFF-0000-0000-0000-000000000004','AAAAAAAA-0000-0000-0000-000000000001','CCCCCCCC-0000-0000-0000-000000000004','TAGS','TEXT'),
        ('FFFFFFFF-0000-0000-0000-000000000005','AAAAAAAA-0000-0000-0000-000000000001','CCCCCCCC-0000-0000-0000-000000000005','XOPTIONS','SELECTBOX'),
        ('FFFFFFFF-0000-0000-0000-000000000006','AAAAAAAA-0000-0000-0000-000000000001','CCCCCCCC-0000-0000-0000-000000000006','JOPTIONS','SELECTBOX')

    INSERT INTO @metaColumns
        ([metaColumnID],[storageTable],[storageColumn],[storageType],[storageSize])
    VALUES
        ('CCCCCCCC-0000-0000-0000-000000000001','METADATA','numericData','numeric',NULL),
        ('CCCCCCCC-0000-0000-0000-000000000002','METADATA','dateData','date',NULL),
        ('CCCCCCCC-0000-0000-0000-000000000003','METADATA','textData','text',NULL),
        ('CCCCCCCC-0000-0000-0000-000000000004','METADATA','GUIDData','guid',NULL),
        ('CCCCCCCC-0000-0000-0000-000000000005','METADATA','xmlData','text',NULL),
        ('CCCCCCCC-0000-0000-0000-000000000006','METADATA','jsonData','text',NULL)

    INSERT INTO @metaDataObjectVersions
        ([metaDataObjectVersionID],[xmlVersionData])
    VALUES
        (1,'<data><active>1</active><created>2019-01-01</created><content>&lt;html&gt;&lt;head&gt;&lt;title&gt; HTML Document&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;HTML document&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content><tags>37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2233,37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2244</tags><xoptions><rows><row id="37c3879f-cf6e-4a5d-bd3c-fcea1d9f2233"><optionValue>Usage 1</optionValue><sorting>1</sorting><selected>1</selected></row></rows></xoptions><joptions>[{"id": 1,"value": "Option 1","selected": true},{"id": 1,"value": "Option 1","selected": false }]</joptions></data>'),          
        (2,'<data><active>0</active><content>&lt;html&gt;&lt;head&gt;&lt;title&gt; HTML Document2&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;HTML document2&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content><tags>37C3879F-CF6E-4A5D-BD3C-FCEA1D9F4433</tags><xoptions/><joptions>[{"id": 3,"value": "Option 3","selected": true}]</joptions></data>')          

    INSERT INTO @metaData
        ([metaDataID],[metaDataObjectVersionID],[metaColumnID],[numericData],[dateData],[textData],[GUIDData],[xmlData],[jsonData])
    VALUES
        (1,1,'CCCCCCCC-0000-0000-0000-000000000001',1,NULL,NULL,NULL,NULL,NULL),
        (2,1,'CCCCCCCC-0000-0000-0000-000000000002',NULL,'2019-01-01',NULL,NULL,NULL,NULL),
        (3,1,'CCCCCCCC-0000-0000-0000-000000000003',NULL,NULL,'&lt;html&gt;&lt;head&gt;&lt;title&gt; HTML Document&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;HTML document&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;',NULL,NULL,NULL),
        (4,1,'CCCCCCCC-0000-0000-0000-000000000004',NULL,NULL,NULL,'37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2233',NULL,NULL),
        (5,1,'CCCCCCCC-0000-0000-0000-000000000004',NULL,NULL,NULL,'37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2244',NULL,NULL),
        (6,1,'CCCCCCCC-0000-0000-0000-000000000005',NULL,NULL,NULL,NULL,'&lt;rows&gt;&lt;row id=&quot;37c3879f-cf6e-4a5d-bd3c-fcea1d9f2233&quot;&gt;&lt;optionValue&gt;Usage 1&lt;/optionValue&gt;&lt;sorting&gt;1&lt;/sorting&gt;&lt;selected&gt;1&lt;/selected&gt;&lt;/row&gt;&lt;/rows&gt;',NULL),--<rows><row id="37c3879f-cf6e-4a5d-bd3c-fcea1d9f2233"><optionValue>Usage 1</optionValue><sorting>1</sorting><selected>1</selected></row></rows>
        (7,1,'CCCCCCCC-0000-0000-0000-000000000006',NULL,NULL,NULL,NULL,NULL,'[{"id": 1,"value": "Option 1","selected": true},{"id": 1,"value": "Option 1","selected": false }]'),
        (8,2,'CCCCCCCC-0000-0000-0000-000000000001',0,NULL,NULL,NULL,NULL,NULL),
        (9,2,'CCCCCCCC-0000-0000-0000-000000000003',NULL,NULL,'&lt;html&gt;&lt;head&gt;&lt;title&gt; HTML Document2&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;HTML document2&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;',NULL,NULL,NULL),
        (10,2,'CCCCCCCC-0000-0000-0000-000000000004',NULL,NULL,NULL,'37C3879F-CF6E-4A5D-BD3C-FCEA1D9F4433',NULL,NULL),
        (11,2,'CCCCCCCC-0000-0000-0000-000000000005',NULL,NULL,NULL,NULL,NULL,NULL),
        (12,2,'CCCCCCCC-0000-0000-0000-000000000006',NULL,NULL,NULL,NULL,NULL,'[{"id": 3,"value": "Option 3","selected": true}]')

    -- EXISTING XML QUERY
    SELECT      metaDataObjectVersionID, 
                CAST(metaObjectVersionData AS XML) AS versionData
    FROM        (
                SELECT      MD.metaDataObjectVersionID,
                        (
                        SELECT          CAST('<' + LOWER(MF.systemRef) + '>' + LEFT(DATA.col, LEN(DATA.col) - 1) + '</' + LOWER(MF.systemRef) + '>' AS XML)
                        FROM            @metaData MD1 
                        INNER JOIN      @metaFields MF ON (MF.metaColumnID = MD1.metaColumnID) 
                        CROSS APPLY    (
                                        SELECT      CASE (SELECT MC.storageColumn FROM @metaColumns MC WHERE MC.metaColumnID = MD2.metaColumnID) 
                                                    WHEN 'numericData' THEN ISNULL(CAST(numericData AS NVARCHAR(MAX)), '') 
                                                    WHEN 'dateData' THEN ISNULL(CAST(dateData AS NVARCHAR(MAX)), '') 
                                                    WHEN 'textData' THEN ISNULL(CAST(textData AS NVARCHAR(MAX)), '')
                                                    WHEN 'guidData' THEN ISNULL(CAST(guidData AS VARCHAR(36)), '')  
                                                    WHEN 'xmlData' THEN ISNULL(CAST(xmlData AS NVARCHAR(MAX)), '') 
                                                    WHEN 'jsonData' THEN ISNULL(CAST(jsonData AS NVARCHAR(MAX)), '') 
                                                    ELSE CAST('' AS VARCHAR(36)) END + ','
                                        FROM        @metaData MD2
                                        WHERE       MD2.metaDataObjectVersionID = MD1.metaDataObjectVersionID AND MD2.metaColumnID = MD1.metaColumnID 
                                        FOR         XML PATH('')
                                        ) AS DATA(col)
                        WHERE           MD1.metaDataObjectVersionID = MD.metaDataObjectVersionID
                        GROUP BY        MF.systemRef, 
                                        DATA.col 
                        FOR             XML PATH(''), ROOT('data')
                        ) AS metaObjectVersionData
            FROM        @metaData MD
            GROUP BY    MD.metaDataObjectVersionID
            ) VW

-- JSON QUERY SO FAR
SELECT A.metaDataObjectVersionID, MDOV.xmlVersionData, N'{' + metaObjectVersionData + N'}'  AS JSONVersionData
FROM        (
            SELECT      MD.metaDataObjectVersionID,
                        (
                        SELECT          QUOTENAME(LOWER(MF.systemRef),'"') + ':' + LEFT(DATA.col, LEN(DATA.col))
                        FROM            @metaData MD1 
                        INNER JOIN      @metaFields MF ON (MF.metaColumnID = MD1.metaColumnID) 
                        CROSS APPLY    (
                                        SELECT     CASE (SELECT MC.storageColumn FROM @metaColumns MC WHERE MC.metaColumnID = MD2.metaColumnID) 
                                                        WHEN 'numericData' THEN CAST(numericData AS NVARCHAR(MAX))
                                                        WHEN 'dateData' THEN  QUOTENAME(CAST(dateData AS NVARCHAR(MAX)),'"')
                                                        WHEN 'textData' THEN QUOTENAME(CAST(textData AS NVARCHAR(4000)),'"')
                                                        WHEN 'guidData' THEN QUOTENAME(CAST(guidData AS VARCHAR(36)),'"')
                                                        WHEN 'xmlData' THEN QUOTENAME(STRING_ESCAPE(CAST(xmlData AS NVARCHAR(MAX)),'json'),'"')
                                                        WHEN 'jsonData' THEN jsonData
                                                        ELSE CAST('' AS VARCHAR(36)) 
                                                    END + ','
                                        FROM        @metaData MD2
                                        WHERE       MD2.metaDataObjectVersionID = MD1.metaDataObjectVersionID AND MD2.metaColumnID = MD1.metaColumnID 
                                        FOR         XML PATH('')
                                        ) AS DATA(col)
                        WHERE           MD1.metaDataObjectVersionID = MD.metaDataObjectVersionID
                        GROUP BY        MF.systemRef, 
                                        DATA.col 
                        FOR             XML PATH('')

                        ) AS metaObjectVersionData
            FROM        @metaData MD
            GROUP BY    MD.metaDataObjectVersionID
            )    A              
JOIN        @metaDataObjectVersions MDOV ON A.metaDataObjectVersionID = MDOV.metaDataObjectVersionID

Я хочу выводбыть правильно отформатированным и действительным JSON.Ниже приведен правильный ожидаемый формат:

   {
  "active": 1,
  "content": "<html><head ><title> HTML Document</title></head><body><p>HTML document</p><a href=\"https://www.w3schools.com\">This is a link</a></body></html>",
  "created": "Jan  1 2019 12:00AM",
  "joptions": [
    {
      "id": 1,
      "value": "Option 1",
      "selected": true
    },
    {
      "id": 1,
      "value": "Option 1",
      "selected": false
    }
  ],
  "tags": [
    "37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2233",
    "37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2244"
  ],
  "xoptions": "<rows><row id=\"37c3879f-cf6e-4a5d-bd3c-fcea1d9f2233\"><optionValue>Usage 1</optionValue> <sorting>1</sorting><selected>1</selected></row></rows>"
}

У меня есть рабочий запрос, но он выглядит немного грязно.Если кто-то может предложить улучшения, пожалуйста, прокомментируйте.

--THIS IS THE QUERY WHICH SEEMS TO WORK!!!
SELECT      MDOV.metaDataObjectVersionID,
            '{' + STRING_AGG(QUOTENAME(LOWER(MD1.systemRef),'"') + ' : ' + 
                CASE  
                    WHEN MD1.storageColumn = 'numericData' THEN CASE WHEN CNT > 1 THEN '[' + MD1.numericData + ']' ELSE MD1.numericData END
                    WHEN MD1.storageColumn = 'dateData' THEN CASE WHEN CNT > 1 THEN '[' + MD1.dateData + ']' ELSE MD1.dateData END
                    WHEN MD1.storageColumn = 'textData' THEN CASE WHEN CNT > 1 THEN '[' + MD1.textData + ']' ELSE MD1.textData END
                    WHEN MD1.storageColumn = 'guidData' THEN CASE WHEN CNT > 1 THEN '[' + MD1.guidData + ']' ELSE MD1.guidData END
                    WHEN MD1.storageColumn = 'xmlData' THEN CASE WHEN CNT > 1 THEN '[' + MD1.xmlData + ']' ELSE MD1.xmlData END
                    WHEN MD1.storageColumn = 'jsonData' THEN CASE WHEN CNT > 1 THEN '[' + MD1.jsonData + ']' ELSE MD1.jsonData END  
                END ,',') + '}'
FROM        @metaDataObjectVersions MDOV 
CROSS APPLY (
            SELECT      MD2.metaDataObjectVersionID,MF.systemRef,MD2.metaColumnID,MC.storageColumn,
                        COUNT(*) AS CNT,
                        STRING_AGG(CONVERT(VARCHAR,numericData), ',') AS numericData,
                        STRING_AGG('"' + CONVERT(VARCHAR, dateData,121) + '"', ',') AS dateData,
                        STRING_AGG('"' + CONVERT(CHAR(36),guidData) + '"', ',') AS guidData,
                        STRING_AGG('"' + STRING_ESCAPE(textData, 'json') + '"', ',') AS textData,
                        STRING_AGG('"' + STRING_ESCAPE(CONVERT(NVARCHAR(MAX),xmlData), 'json') + '"', ',') AS xmlData,
                        STRING_AGG('"' + STRING_ESCAPE(jsonData, 'json') + '"', ',') AS jsonData
            FROM        @metaData MD2
            JOIN        @metaFields MF ON MD2.metaColumnID = MF.metaColumnID
            JOIN        @metaColumns MC ON MD2.metaColumnID = MC.metaColumnID
            WHERE       MD2.metaDataObjectVersionID = MDOV.metaDataObjectVersionID
            GROUP BY    MD2.metaDataObjectVersionID,MF.systemRef,MD2.metaColumnID,MC.storageColumn
            ) MD1
GROUP BY    MDOV.metaDataObjectVersionID
ORDER BY    MDOV.metaDataObjectVersionID

Ответы [ 2 ]

0 голосов
/ 04 июня 2019

Извините, но это не то, что я просил в вопросе.Произведенный вами xml на самом деле недействителен.Я уже нашел решение в Джейсоне, которое работает хорошо.Спасибо, так или иначе, я закрою это сейчас.

0 голосов
/ 04 июня 2019

Я потратил некоторое время, но есть несколько проблем. Пожалуйста, добавьте некоторые детали к вашему вопросу:

XML-экранирование действительно странно. Будет ли типизированный XML-столбец представлять собой строку с escape-последовательностями или настоящий XML? Самая сложная часть - это именование столбцов. XML и JSON должны знать эти имена заранее. Вы используете трюк с CAST('<' + SomeName + '>' AS XML), который может быть обходным путем, но вы открываете дверь в ад этим ...

Ваш собственный запрос представляет здесь огромную проблему. Вы получаете что-то вроде

<content>&amp;lt;html&amp;gt;&amp;lt;head&amp...

Вы действительно хотите хранить строку XML в XML ???

Другая проблема, когда вы пытаетесь использовать приведение между XML, строкой и JSON, - это тип кавычек. Двойные кавычки не могут быть использованы внутри JSON напрямую (их нужно экранировать), но XML будет хорошо работать и с одинарными кавычками ... Для этого потребуется много IF/CASE ...

Есть несколько тегов с несколькими значениями массива (например, "tags":[Val1,Val2]), которые не поддерживаются FOR JSON.

По крайней мере, я уверен, ваш запрос может быть упрощен. Попробуйте это:

SELECT ov.metaDataObjectVersionID
      ,(
            SELECT CAST(
                    CONCAT('<'
                           ,LOWER(MF.systemRef)
                           ,'>'
                           ,(SELECT 
                            CONCAT(numericData
                                  ,dateData
                                  ,CAST(textData AS XML).value('.','nvarchar(max)')
                                  ,GUIDData
                                  ,xmlData.value('.','nvarchar(max)')
                                  ,jsonData
                                  ,'') FOR XML PATH(''))
                           ,'</'
                           ,LOWER(MF.systemRef)
                           ,'>') AS XML)
            FROM            @metaData MD1 
            INNER JOIN      @metaFields MF ON (MF.metaColumnID = MD1.metaColumnID) 
            WHERE           MD1.metaDataObjectVersionID = ov.metaDataObjectVersionID
            FOR             XML PATH(''), ROOT('data'),TYPE
        ) AS metaObjectVersionDataXML
FROM        @metaDataObjectVersions ov;

Результат для metaDataObjectVersionID=1

<data>
  <active>1</active>
  <created>Jan  1 2019 12:00AM</created>
  <content>&lt;html&gt;&lt;head&gt;&lt;title&gt; HTML Document&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;HTML document&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content>
  <tags>37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2233</tags>
  <tags>37C3879F-CF6E-4A5D-BD3C-FCEA1D9F2244</tags>
  <xoptions>&lt;rows&gt;&lt;row id="37c3879f-cf6e-4a5d-bd3c-fcea1d9f2233"&gt;&lt;optionValue&gt;Usage 1&lt;/optionValue&gt;&lt;sorting&gt;1&lt;/sorting&gt;&lt;selected&gt;1&lt;/selected&gt;&lt;/row&gt;&lt;/rows&gt;</xoptions>
  <joptions>[{"id": 1,"value": "Option 1","selected": true},{"id": 1,"value": "Option 1","selected": false }]</joptions>
</data>

Ну, tags не выглядят так, как вам нужно, но это можно решить ...

Я бы попытался создать JSON из собственного XML с помощью операторов XQuery / FLWOR, но прежде чем я смогу сделать больше, я прошу вас ответить на мои вопросы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...