【问题标题】:Convert a query producing XML so it produces JSON转换生成 XML 的查询,使其生成 JSON
【发布时间】:2019-05-31 13:40:16
【问题描述】:

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

我需要这个查询的一个版本来生成 JSON 格式的输出

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

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

2] XML 和 JSON 列数据未正确格式化为有效的 JSON。

我没有尝试过使用 OPENJSON 或 CTE,但愿意尝试一下。

我在 SQL Server 2017(见下文)中有一个用于创建 XMLdata 的查询,我需要更改它以将结果创建为 JSON。尝试了很多东西,但不太能得到它。感谢任何帮助。

已尝试查询 JSON 版本,但存在一些问题。 1] 我需要将 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

【问题讨论】:

  • 如果您使用的是 SQL Server 2017,您可以访问 FOR JSON: Format Query Results as JSON with FOR JSON (SQL Server)。我建议从那里开始。请先尝试一下,如果遇到困难,请向我们展示您的尝试,解释为什么它们不起作用,并展示您追求的结果。
  • 已经尝试使用 FOR JSON,但我无法让它工作。问题是数据是垂直存储的(因此是 CROSS APPLY)。有些列有多行数据,因此需要转换为 JSON 数组等。请查看我提供的查询,您应该能够明白我的意思。
  • 我在您的帖子中看不到任何对FOR JSON 的引用来查看?也许您忘记粘贴该特定代码?请参阅我之前对我们需要什么的评论。
  • 我曾尝试使用 FOR JSON,但我无法让它工作,我可以尝试找到我之前的尝试,但不确定它的用途,因为它不起作用。您不能只将 FOR XML 替换为 FOR JSON,因为存在差异,这意味着查询必须发生重大变化,这就是我坚持的重点。
  • 请发布您的尝试,并向我们展示您所追求的预期结果。

标签: sql json sql-server xml


【解决方案1】:

我已经投入了一些时间,但存在多个问题。请在您的问题中添加一些详细信息:

XML 转义真的很奇怪,XML 类型的列是带有转义序列的字符串还是真正的 XML?最困难的部分是列命名。 XML 和 JSON 需要提前知道这些名称。你使用 CAST('&lt;' + SomeName + '&gt;' 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,但在我尝试做更多事情之前,我请你回答我的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-09
    • 2015-12-13
    • 1970-01-01
    • 1970-01-01
    • 2015-12-06
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多