转换查询以生成XML,以便生成JSON

时间:2019-05-31 13:40:16

标签: sql json sql-server xml

我有一个查询(见下文),用于创建XML输出。该查询处理的是matadata(EAV)系统,该系统具有多种列类型和数据。

我需要此查询的版本以将输出生成为JSON

我已经试过了,但是遇到了问题。

1]我需要将nvarchar(max)列数据转换为nvarchar(4000),这并不理想,这与我认为的串联有关。

2] XML和JSON列数据的格式没有正确设置为有效JSON。

我没有尝试使用OPENJSON或CTE,但愿意尝试任何事情。

我在SQL Server 2017中有一个查询(请参阅下文),该查询曾用于创建XMLdata,我需要更改它以将结果创建为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 :(得分:0)

我花了一些时间,但是有很多问题。请在您的问题中添加一些详细信息:

XML的转义真的很奇怪,XML类型的列是带转义序列的字符串还是真实的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看起来并不像您所需要的,但这可以解决...

我会尝试使用XQuery / FLWOR语句从本机XML中创建JSON,但是在尝试做更多事情之前,请您回答我的问题。

答案 1 :(得分:0)

对不起,但这不是我在问题中要的。您产生的xml实际上是无效的。我已经在Jason中找到了很好的解决方案。无论如何,谢谢,我现在将关闭它。