如何将子 JSON 结构转换为 C# 对象图

How to convert child JSON structure to C# object graph

我正在使用 SQL Server 2016 的 FOR JSON 将 SQL Server SELECT 语句转换为 JSON 字符串。我的意图是让 C# 将生成的 JSON 字符串转换为一个对象。

SQL服务器服务器select声明:

SELECT
    Id AS PersonId, 
    FirstName, 
    LastName, 
    Name, 
    Birthday,
    (
        SELECT
            PhoneNumber.PhoneNumberTypeId, 
            PhoneNumber.PhoneNumber             
        FROM            
            PhoneNumber 
            INNER JOIN PhoneNumberType ON PhoneNumber.PhoneNumberTypeId = PhoneNumberType.Id
        WHERE
            PhoneNumber.PersonId = Person.Id
        FOR JSON PATH,INCLUDE_NULL_VALUES       
    ) AS PhoneNumberList
FROM            
    Person
ORDER BY
    LastName
FOR JSON PATH,INCLUDE_NULL_VALUES

C# 对象我正在尝试将数据转换为:

public class PersonDataContract : IPersonDataContract
{
    public Int32 PersonId { get; set; }
    public String FirstName { get; set; }
    public String LastName { get; set; }
    public String Name { get; set; }
    public DateTime? Birthday { get; set; }
    public Int32 Age { get; }
    public IList<IPhoneNumberDataContract> PhoneNumberList { get; set; }

    public PersonDataContract()
    {
        PhoneNumberList = new List<IPhoneNumberDataContract>();
    }
}

原始 JSON:

[
    {
        "PersonId": 5,
        "FirstName": "Squire",
        "LastName": "Escrow",
        "Name": "Squire Escrow",
        "Birthday": "2001-12-12",
        "PhoneNumberList": "[{\"PhoneNumberTypeId\":1,\"PhoneNumber\":\"8129091423\"}]"
    }, 
    {
        "PersonId": 6,
        "FirstName": "Vilhelm",
        "LastName": "Huldsputh",
        "Name": "Vilhelm Huldsputh",
        "Birthday": "1953-11-13",
        "PhoneNumberList": "[{\"PhoneNumberTypeId\":1,\"PhoneNumber\":\"9871237171\"},{\"PhoneNumberTypeId\":2,\"PhoneNumber\":\"8189991212\"}]"
    }
]

将原始 JSON 转换为对象的代码:

...
using (DbDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{

    while (reader.Read())
    {
        var jsonString = reader.GetValue(0).ToString();

        var list = JsonConvert.DeserializeObject<List<PersonDataContract>>(jsonString);
    }

    reader.Close();
}

只要我从原始 JSON 中删除 PhoneNumberList,此过程就有效。我认为问题是我在我的 SQL 语句中使用了 2 'FOR JSON PATH's(一个用于 PhoneNumberList 本身,一个用于整个 SELECT 语句)这导致 PhoneNumberList被转换为未转换为数组的字符串。

如何将表示 phone 个数字集合的子字符串转换为 IPhoneNumberDataContract 数组?

这是 中涵盖的 the FAQ:

Question. I have JSON text stored in a table column. I want to include it in the output of FOR JSON. But FOR JSON escapes all characters in the JSON, so I'm getting a JSON string instead of a nested object [..]

Answer. JSON stored in a text column or a literal is treated like any text. That is, it's surrounded with double quotes and escaped. If you want to return an unescaped JSON object, pass the JSON column as an argument to the JSON_QUERY function [..]

它没有明确提到任何使用 FOR JSON 的非平凡子查询也会产生转义值,并且以这种方式需要 JSON_QUERY。所以使用

SELECT
    Id AS PersonId, 
    FirstName, 
    LastName, 
    Name, 
    Birthday,
    JSON_QUERY(
        SELECT
            ...
        FOR JSON PATH,INCLUDE_NULL_VALUES       
    ) AS PhoneNumberList

"non-trivial",我的意思是这样的查询不需要 JSON_QUERY:

SELECT 
    'Foo' AS Foo, (
        SELECT 1 AS A 
        FOR JSON PATH
    ) AS Bar
FOR JSON PATH

Bar 不会在这里转义,因为优化器显然记得它正在处理 JSON。我还没有找到一套一致的规则来管理这个 (),甚至不清楚这是有意为之的行为还是一个错误,但是 JSON_QUERY 无论如何都会帮助你.它可能应该始终被使用,以防止从 "simple" 到 "complex" 的查询突然需要它。