网 5 System.Text.Json.JsonSerializer returns 不完整 json
Net 5 System.Text.Json.JsonSerializer returns incomplete json
我有一个 webapi 控制器和如下方法通过 Entity Framework 检索 SQL 服务器数据库中的记录。 tblQuestion 有几个指向 tblCodeSet 的外键,它有一个自引用的 CodeSetParent 属性。 webapi 在 Swagger UI 和 Postman 中工作。但是,当使用 APIAgent 中的 HttpClient 调用它时,DeserializeAsync<>() 失败并出现错误
"Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. Path: $.$values"
从 Postman 下载的 json 有 2013 行,但只有一条记录是序列化的,具有非常深的导航引用。更糟糕的是 json 数组没有关闭。我想知道是否是因为 tblQuestion 具有超过 json 有效负载的深度导航。感谢您的帮助。
namespace IPRehabModel
{
public partial class TblCodeSet
{
public TblCodeSet()
{
InverseCodeSetParentNavigation = new HashSet<TblCodeSet>();
TblAnswer = new HashSet<TblAnswer>();
TblQuestionStage = new HashSet<TblQuestionStage>();
TblQuestionAnswerCodeSetFkNavigation = new HashSet<TblQuestion>();
TblQuestionFormFkNavigation = new HashSet<TblQuestion>();
TblQuestionFormSectionFkNavigation = new HashSet<TblQuestion>();
}
public int CodeSetId { get; set; }
public int? CodeSetParent { get; set; }
public string CodeValue { get; set; }
public string CodeDescription { get; set; }
public int? HierarchyType { get; set; }
public bool? Active { get; set; }
public int? FyConstraint { get; set; }
public int? SortOrder { get; set; }
public string Comment { get; set; }
public virtual TblCodeSet CodeSetParentNavigation { get; set; }
public virtual ICollection<TblCodeSet> InverseCodeSetParentNavigation { get; set; }
public virtual ICollection<TblAnswer> TblAnswer { get; set; }
public virtual ICollection<TblQuestionStage> TblQuestionStage { get; set; }
public virtual ICollection<TblQuestion> TblQuestionAnswerCodeSetFkNavigation { get; set; }
public virtual ICollection<TblQuestion> TblQuestionFormFkNavigation { get; set; }
public virtual ICollection<TblQuestion> TblQuestionFormSectionFkNavigation { get; set; }
}
}
namespace IPRehabModel {
public partial class TblQuestion {
public TblQuestion() {
TblAnswer = new HashSet < TblAnswer > ();
TblQuestionInstruction = new HashSet < TblQuestionInstruction > ();
}
public int QuestionId {
get;
set;
}
public string QuestionKey {
get;
set;
}
public int ? Order {
get;
set;
}
public string QuestionTitle {
get;
set;
}
public string Question {
get;
set;
}
public string GroupTitle {
get;
set;
}
public int FormFk {
get;
set;
}
public int ? FormSectionFk {
get;
set;
}
public int AnswerCodeSetFk {
get;
set;
}
public bool ? BranchingPoint {
get;
set;
}
public bool ? MultiChoice {
get;
set;
}
public virtual TblCodeSet AnswerCodeSetFkNavigation {
get;
set;
}
public virtual TblCodeSet FormFkNavigation {
get;
set;
}
public virtual TblCodeSet FormSectionFkNavigation {
get;
set;
}
public virtual ICollection < TblAnswer > TblAnswer {
get;
set;
}
public virtual ICollection < TblQuestionInstruction > TblQuestionInstruction {
get;
set;
}
public virtual ICollection < TblQuestionStage > TblQuestionStage {
get;
set;
}
}
}
namespace IPRehabWebAPI2.Controllers {
[Route("api/[controller]")]
[ApiController]
public class QuestionsController: ControllerBase {
private readonly IQuestionRepository _questionRepository;
public QuestionsController(IQuestionRepository questionRepository) {
_questionRepository = questionRepository;
}
public ActionResult < IEnumerable < TblQuestion >> GetInitStage() {
var questions = _questionRepository.FindByCondition(x => x.TblQuestionStage
.Where(s => s.QuestionIdFk == x.QuestionId &&
s.StageFkNavigation.CodeValue == "Initial").Any());
return Ok(questions);
}
}
}
namespace IPRehab.Helpers {
public static class APIAgent {
public static async Task < IEnumerable < TblQuestion >> StreamWithSystemTextJson(Uri uri, JsonSerializerOptions options) {
using
var client = new HttpClient();
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using
var httpResponse = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
httpResponse.EnsureSuccessStatusCode();
if (httpResponse.Content is object && httpResponse.Content.Headers.ContentType.MediaType == "application/json") {
var contentStream = await httpResponse.Content.ReadAsStreamAsync();
try {
return await JsonSerializer.DeserializeAsync < IEnumerable < TblQuestion >> (contentStream, options);
} catch (JsonException ex) // Invalid JSON
{
Console.WriteLine("Invalid JSON.");
Console.WriteLine(ex.Message);
}
} else {
Console.WriteLine("HTTP Response was invalid and cannot be deserialised.");
}
return null;
}
}
}
我刚刚读到,EF 在路径上遇到空值 属性 时停止序列化存在问题。我没有让 EF 进行深度导航,而是创建了一个 DTO 并使用 .Select() 将其混合以投影域属性。
var questions = _questionRepository.FindByCondition(x => x.Active.Value != false && x.TblQuestionStage
.Where(s => s.QuestionIdFk == x.QuestionId &&
s.StageFkNavigation.CodeValue == "Initial").Any())
.Select(q => HydrateDTO.Hydrate(q))
.ToList().OrderBy(o => o.DisplayOrder).ThenBy(o => o.QuestionKey);
using IPRehabModel;
using IPRehabWebAPI2.Models;
using System.Linq;
namespace IPRehabWebAPI2.Helpers {
/// <summary>
/// Hydrate the DTO with selected domain properties
/// </summary>
public class HydrateDTO {
//ToDo: should use AutoMapper
public static QuestionDTO Hydrate(TblQuestion q) {
return new QuestionDTO {
QuestionID = q.QuestionId,
QuestionKey = q.QuestionKey,
QuestionTitle = q.QuestionTitle,
Question = q.Question,
GroupTitle = q.GroupTitle,
AnswerCodeSetID = q.AnswerCodeSetFk,
DisplayOrder = q.Order,
ChoiceList = q.AnswerCodeSetFkNavigation.InverseCodeSetParentNavigation
.Select(s => new CodeSetDTO {
CodeSetID = s.CodeSetId,
CodeSetParent = s.CodeSetParent,
CodeValue = s.CodeValue,
CodeDescription = s.CodeDescription,
Comment = s.Comment
}).ToList()
};
}
}
}
我有一个 webapi 控制器和如下方法通过 Entity Framework 检索 SQL 服务器数据库中的记录。 tblQuestion 有几个指向 tblCodeSet 的外键,它有一个自引用的 CodeSetParent 属性。 webapi 在 Swagger UI 和 Postman 中工作。但是,当使用 APIAgent 中的 HttpClient 调用它时,DeserializeAsync<>() 失败并出现错误
"Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed. Path: $.$values"
从 Postman 下载的 json 有 2013 行,但只有一条记录是序列化的,具有非常深的导航引用。更糟糕的是 json 数组没有关闭。我想知道是否是因为 tblQuestion 具有超过 json 有效负载的深度导航。感谢您的帮助。
namespace IPRehabModel
{
public partial class TblCodeSet
{
public TblCodeSet()
{
InverseCodeSetParentNavigation = new HashSet<TblCodeSet>();
TblAnswer = new HashSet<TblAnswer>();
TblQuestionStage = new HashSet<TblQuestionStage>();
TblQuestionAnswerCodeSetFkNavigation = new HashSet<TblQuestion>();
TblQuestionFormFkNavigation = new HashSet<TblQuestion>();
TblQuestionFormSectionFkNavigation = new HashSet<TblQuestion>();
}
public int CodeSetId { get; set; }
public int? CodeSetParent { get; set; }
public string CodeValue { get; set; }
public string CodeDescription { get; set; }
public int? HierarchyType { get; set; }
public bool? Active { get; set; }
public int? FyConstraint { get; set; }
public int? SortOrder { get; set; }
public string Comment { get; set; }
public virtual TblCodeSet CodeSetParentNavigation { get; set; }
public virtual ICollection<TblCodeSet> InverseCodeSetParentNavigation { get; set; }
public virtual ICollection<TblAnswer> TblAnswer { get; set; }
public virtual ICollection<TblQuestionStage> TblQuestionStage { get; set; }
public virtual ICollection<TblQuestion> TblQuestionAnswerCodeSetFkNavigation { get; set; }
public virtual ICollection<TblQuestion> TblQuestionFormFkNavigation { get; set; }
public virtual ICollection<TblQuestion> TblQuestionFormSectionFkNavigation { get; set; }
}
}
namespace IPRehabModel {
public partial class TblQuestion {
public TblQuestion() {
TblAnswer = new HashSet < TblAnswer > ();
TblQuestionInstruction = new HashSet < TblQuestionInstruction > ();
}
public int QuestionId {
get;
set;
}
public string QuestionKey {
get;
set;
}
public int ? Order {
get;
set;
}
public string QuestionTitle {
get;
set;
}
public string Question {
get;
set;
}
public string GroupTitle {
get;
set;
}
public int FormFk {
get;
set;
}
public int ? FormSectionFk {
get;
set;
}
public int AnswerCodeSetFk {
get;
set;
}
public bool ? BranchingPoint {
get;
set;
}
public bool ? MultiChoice {
get;
set;
}
public virtual TblCodeSet AnswerCodeSetFkNavigation {
get;
set;
}
public virtual TblCodeSet FormFkNavigation {
get;
set;
}
public virtual TblCodeSet FormSectionFkNavigation {
get;
set;
}
public virtual ICollection < TblAnswer > TblAnswer {
get;
set;
}
public virtual ICollection < TblQuestionInstruction > TblQuestionInstruction {
get;
set;
}
public virtual ICollection < TblQuestionStage > TblQuestionStage {
get;
set;
}
}
}
namespace IPRehabWebAPI2.Controllers {
[Route("api/[controller]")]
[ApiController]
public class QuestionsController: ControllerBase {
private readonly IQuestionRepository _questionRepository;
public QuestionsController(IQuestionRepository questionRepository) {
_questionRepository = questionRepository;
}
public ActionResult < IEnumerable < TblQuestion >> GetInitStage() {
var questions = _questionRepository.FindByCondition(x => x.TblQuestionStage
.Where(s => s.QuestionIdFk == x.QuestionId &&
s.StageFkNavigation.CodeValue == "Initial").Any());
return Ok(questions);
}
}
}
namespace IPRehab.Helpers {
public static class APIAgent {
public static async Task < IEnumerable < TblQuestion >> StreamWithSystemTextJson(Uri uri, JsonSerializerOptions options) {
using
var client = new HttpClient();
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using
var httpResponse = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
httpResponse.EnsureSuccessStatusCode();
if (httpResponse.Content is object && httpResponse.Content.Headers.ContentType.MediaType == "application/json") {
var contentStream = await httpResponse.Content.ReadAsStreamAsync();
try {
return await JsonSerializer.DeserializeAsync < IEnumerable < TblQuestion >> (contentStream, options);
} catch (JsonException ex) // Invalid JSON
{
Console.WriteLine("Invalid JSON.");
Console.WriteLine(ex.Message);
}
} else {
Console.WriteLine("HTTP Response was invalid and cannot be deserialised.");
}
return null;
}
}
}
我刚刚读到,EF 在路径上遇到空值 属性 时停止序列化存在问题。我没有让 EF 进行深度导航,而是创建了一个 DTO 并使用 .Select() 将其混合以投影域属性。
var questions = _questionRepository.FindByCondition(x => x.Active.Value != false && x.TblQuestionStage
.Where(s => s.QuestionIdFk == x.QuestionId &&
s.StageFkNavigation.CodeValue == "Initial").Any())
.Select(q => HydrateDTO.Hydrate(q))
.ToList().OrderBy(o => o.DisplayOrder).ThenBy(o => o.QuestionKey);
using IPRehabModel;
using IPRehabWebAPI2.Models;
using System.Linq;
namespace IPRehabWebAPI2.Helpers {
/// <summary>
/// Hydrate the DTO with selected domain properties
/// </summary>
public class HydrateDTO {
//ToDo: should use AutoMapper
public static QuestionDTO Hydrate(TblQuestion q) {
return new QuestionDTO {
QuestionID = q.QuestionId,
QuestionKey = q.QuestionKey,
QuestionTitle = q.QuestionTitle,
Question = q.Question,
GroupTitle = q.GroupTitle,
AnswerCodeSetID = q.AnswerCodeSetFk,
DisplayOrder = q.Order,
ChoiceList = q.AnswerCodeSetFkNavigation.InverseCodeSetParentNavigation
.Select(s => new CodeSetDTO {
CodeSetID = s.CodeSetId,
CodeSetParent = s.CodeSetParent,
CodeValue = s.CodeValue,
CodeDescription = s.CodeDescription,
Comment = s.Comment
}).ToList()
};
}
}
}