网 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()
      };
    }
  }
}