【问题标题】:Looping through array of objects in SQL Server and getting error: JSON text is not properly formatted循环遍历 SQL Server 中的对象数组并出现错误:JSON 文本格式不正确
【发布时间】:2019-02-18 16:34:13
【问题描述】:

我正在尝试遍历 JSON 对象 @files 中的对象数组, 并将数组中的每个对象插入到表中,但出现此错误:

JSON 文本格式不正确。意外字符“。”在位置 0 找到。

JSON 使用 JSONLint 是有效的,所以我知道它不是我声明的对象,除非我错了。选择错误时,它会在 OPENJSON WITH() 语句中突出显示:

file_name NVARCHAR(100) '$.fileName',

ALTER PROCEDURE files_uploadAll
    @document_id INT OUTPUT,
    @files NVARCHAR(MAX)

/*
DECLARE @document_id INT
DECLARE @files NVARCHAR(MAX) = N'{  
      "files": [
      {  
            "noteId": 1,
            "documentTitle": "doc1",
            "fileName": "doc1.pdf",
            "fileExtension": "pdf",
            "mimeType": "application/pdf",
            "documentTypeCd": "MSA",
            "userId": 1,
            "url": "http://www.url.com"
       },
       {  
            "noteId": 2,
            "documentTitle": "doc2",
            "fileName": "doc2.doc",
            "fileExtension": "doc",
            "mimeType": "application/msword",
            "documentTypeCd": "MSA",
            "userId": 1,
            "url": "http://www.url.com"
       }
    ]           
 }';
 EXECUTE files_uploadAll @files=@files, @document_id=@document_id OUTPUT
*/

AS

DECLARE @filesArray NVARCHAR(MAX)
SET @filesArray = (SELECT '$.files' FROM OPENJSON(@files))

DECLARE @filesList NVARCHAR(MAX), @i int
SELECT @i=0, @filesList = @filesArray

WHILE (@i < LEN(@filesList))
BEGIN
    DECLARE @item NVARCHAR(MAX)
    SELECT @item = SUBSTRING(@filesList, @i, CHARINDEX(',',@filesList,@i)-@i)

    INSERT INTO documents
    (note_id, document_title, file_name, file_extension, mime_type, document_type_cd, user_id, url)
    SELECT note_id, document_title, file_name, file_extension, mime_type, document_type_cd, user_id, url
    FROM OPENJSON(@item)
    WITH (
        note_id INT '$.noteId',
        document_title NVARCHAR(100) '$.documentTitle',
        file_name NVARCHAR(100) '$.fileName',
        file_extension NVARCHAR(25) '$.fileExtension',
        mime_type NVARCHAR(50) '$.mimeType',
        document_type_cd CHAR(5) '$.documentTypeCd',
        user_id int '$.userId',
        url NVARCHAR(1000) '$.url'
    )
    SET @document_id=SCOPE_IDENTITY()

    SET @i = CHARINDEX(',',@filesList,@i)+1
    IF(@i = 0) SET @i = LEN(@filesList)
END

【问题讨论】:

  • 这是 SQL Server 2017 和兼容级别 130 或更高版本吗?

标签: arrays json sql-server tsql ssms


【解决方案1】:

不需要任何循环,也不需要更改输入。这可以通过以下查询轻松解决:

SELECT *
FROM OPENJSON(JSON_QUERY(@files,'$.files'))
WITH (
    note_id INT '$.noteId',
    document_title NVARCHAR(100) '$.documentTitle',
    file_name NVARCHAR(100) '$.fileName',
    file_extension NVARCHAR(25) '$.fileExtension',
    mime_type NVARCHAR(50) '$.mimeType',
    document_type_cd CHAR(5) '$.documentTypeCd',
    user_id int '$.userId',
    url NVARCHAR(1000) '$.url'
);

返回的是一个简单的结果集,可用于任何操作:

+---------+----------------+-----------+----------------+--------------------+------------------+---------+--------------------+
| note_id | document_title | file_name | file_extension | mime_type          | document_type_cd | user_id | url                |
+---------+----------------+-----------+----------------+--------------------+------------------+---------+--------------------+
| 1       | doc1           | doc1.pdf  | pdf            | application/pdf    | MSA              | 1       | http://www.url.com |
+---------+----------------+-----------+----------------+--------------------+------------------+---------+--------------------+
| 2       | doc2           | doc2.doc  | doc            | application/msword | MSA              | 1       | http://www.url.com |
+---------+----------------+-----------+----------------+--------------------+------------------+---------+--------------------+

我使用JSON_QUERY 进入$.filesOPENJSON 将返回 对象数组,而 WITH 子句将对象转换为命名和类型列。

【讨论】:

  • 这适用于返回结果集,但我需要将数据插入到包含外键的表中,所以我使用了下面的 Pranav 解决方案。我发现移除包装不会干扰我要发送的数据。
  • @hypnagogia 您可以直接从该语句中一次性插入所有内容吗?通常,此类事情是针对临时表完成的(两步导入)。
  • 它确实有效,我能够通过使用 AJAX 调用多路复用插入到表中,我发送了一个包含对象数组的请求并以正确的顺序获取所有数据。
【解决方案2】:

试试这个:

ALTER PROCEDURE files_uploadAll

@document_id INT OUTPUT,
@files NVARCHAR(MAX)



AS
BEGIN

INSERT INTO documents
    (note_id, document_title, file_name, file_extension, mime_type, document_type_cd, user_id, url)
    SELECT note_id, document_title, file_name, file_extension, mime_type, document_type_cd, user_id, url
    FROM OPENJSON(@files)
    WITH (
        note_id INT '$.noteId',
        document_title NVARCHAR(100) '$.documentTitle',
        file_name NVARCHAR(100) '$.fileName',
        file_extension NVARCHAR(25) '$.fileExtension',
        mime_type NVARCHAR(50) '$.mimeType',
        document_type_cd CHAR(5) '$.documentTypeCd',
        user_id int '$.userId',
        url NVARCHAR(1000) '$.url'
    )
    SET @document_id=SCOPE_IDENTITY()

END

执行:

DECLARE @d INT
DECLARE @f NVARCHAR(MAX) = N'[
      {  
            "noteId": 1,
            "documentTitle": "doc1",
            "fileName": "doc1.pdf",
            "fileExtension": "pdf",
            "mimeType": "application/pdf",
            "documentTypeCd": "MSA",
            "userId": 1,
            "url": "http://www.url.com"
       },
       {  
            "noteId": 2,
            "documentTitle": "doc2",
            "fileName": "doc2.doc",
            "fileExtension": "doc",
            "mimeType": "application/msword",
            "documentTypeCd": "MSA",
            "userId": 1,
            "url": "http://www.url.com"
       }
    ]';
 EXECUTE files_uploadAll @files=@f, @document_id=@d OUTPUT

如果您注意到我刚刚添加了数组作为参数并且没有循环,我们可以插入具有性能的数据。

【讨论】:

  • 您要求他们更改 json 文件。您必须使用他们的 json 文件提供解决方案。
  • 不更改 json 而是删除多余的包装器,作为一个开发人员,我们知道在应用程序层删除它更容易且高效,因为序列化是一个具有名为 files 的文件集合的实体。只需向 db 少发送一个级别,在应用程序级别什么都不做。如果无法更改代码,我可以更改答案,但这样更有效
  • 我非常不同意! 1)在查询级别解决这个问题很容易,请参阅我的答案。 2) 很多时候我们无法更改发送应用程序。 3) 更多时候我们不想更改发送应用程序。 4) 我们不想混合不同样式的数据 5) 并且 - 假设 - 已经存在数据并且 6) 你不知道,如果这个包装器是冗余
  • 公平地说,两者都还只是假设。取决于您如何看待,我从 ui 到代码到 db 端到端工作,因此在调用存储过程时似乎对 remove redundant 包装器更具执行性(不需要更改类 obj.files 而不是 obj),因为从长远来看我们必须保持混乱。在进行性能调整时,始终只发送您需要的内容。
  • 这很完美!通过删除该包装器来处理数据要容易得多,并且它很容易添加到我的表中。谢谢!
【解决方案3】:

由于您的 JSON 中有根元素,您需要使用根元素调用 OPENJSON,如下所示。你现在不会得到错误。

Refer to JSON support in SQL Server 2016

DECLARE @document_id INT
DECLARE @files NVARCHAR(MAX) = N'{  
      "files": [
      {  
            "noteId": 1,
            "documentTitle": "doc1",
            "fileName": "doc1.pdf",
            "fileExtension": "pdf",
            "mimeType": "application/pdf",
            "documentTypeCd": "MSA",
            "userId": 1,
            "url": "http://www.url.com"
       },
       {  
            "noteId": 2,
            "documentTitle": "doc2",
            "fileName": "doc2.doc",
            "fileExtension": "doc",
            "mimeType": "application/msword",
            "documentTypeCd": "MSA",
            "userId": 1,
            "url": "http://www.url.com"
       }
    ] '          

SELECT * FROM OPENJSON(@files,'$.files');

【讨论】:

  • 这可能有效,但OPENJSON 将返回一个表,这对于根元素来说是相当多的开销。你可能想检查我的答案。最好在这个返回值上使用JSON_QUERY 然后OPENJSON
猜你喜欢
  • 2013-09-21
  • 2019-12-17
  • 1970-01-01
  • 2016-07-28
  • 2021-09-06
  • 1970-01-01
  • 1970-01-01
  • 2015-06-27
相关资源
最近更新 更多