【问题标题】:dotnet core web API JSON response has unnecessary elementsdotnet core web API JSON 响应包含不必要的元素
【发布时间】:2019-03-26 03:39:20
【问题描述】:

我懒得将我的表数据加载到 JSON 结果中并将它们发送到前端应用程序。但是当我得到这些数据时,我注意到响应中存在不必要的元素,即空元素。因此,我的 PUT 或更新操作不适用于这些内部 JSON 属性。

   {
           "image":null,
           "paragraph":null,
           "question":{
              "grid":null,
              "elements":[

              ],
              "offeredAnswers":[

              ],
              "lazyLoader":{

              },
              "id":"1",
              "text":"How can we serve you better?",
              "type":"textarea",
              "questionRequired":false,
              "pageFlowModifier":false,
              "gridId":null,
              "min":null,
              "max":null
           },
           "lazyLoader":{

           }
   }

如果我更改text 的值,它不会更新,但如果我更改paragraph,那么它将在数据库中更改。

这里有一个名为lazyLoader 的新属性,我也需要删除它。和elementsofferedAnswers 真的不需要,因为它们是空的。我通过将virtual 关键字添加到引用的类来实现延迟加载。

public partial class Questions
{
    public Questions()
    {
        Elements = new HashSet<Elements>();
        OfferedAnswers = new HashSet<OfferedAnswers>();
    }


    public string Id { get; set; }
    public string Text { get; set; }
    public string Type { get; set; }
    public bool QuestionRequired { get; set; }
    public bool PageFlowModifier { get; set; }
    public int? GridId { get; set; }
    public long? Min { get; set; }
    public long? Max { get; set; }

    public virtual Grids Grid { get; set; }
    public virtual ICollection<Elements> Elements { get; set; }
    public virtual ICollection<OfferedAnswers> OfferedAnswers { get; set; }
}

我在Startup.cs 文件中有这一行来停止引用循环处理,因为没有它,POST 操作不起作用,因为我发布的JSON 对象非常复杂,并且里面有引用循环.

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

我已启用延迟加载代理。

services.AddDbContext<RDS_Context>
            (options => options.UseLazyLoadingProxies().UseSqlServer(connection));

【问题讨论】:

  • 你想从你的json中删除空对象{}和空数组[]吗?
  • 是的,elementsofferedAnswers 是数据库中具有空值的实际表,因此这些响应是可以理解的。 lazyLoader 不是任何表中的属性。我主要想删除它。
  • 那么null values 键呢?他们也删除或保留?
  • 我们可以保留这些,因为我需要将它们表示为 null。
  • 我想你忘了在下面查看我的答案,查看它可能对你有帮助:)

标签: c# json .net-core asp.net-core-webapi


【解决方案1】:

您可以将您的数据库实体模型与您希望在前端显示的模型分开。映射您需要并希望包含到新前端模型中的实体模型的属性。

在不实现新模型的情况下,一种更快的方法是使用匿名对象,例如:

myDbContext.MyObjects.Select(x => new { Prop1 = x.Property1, Prop2 = x.Property2 });

如果您的实体类中确实有 LazyLoader 属性(例如,注入 ILazyLoader as described here),您可以改为使用 JsonIgnore attribtue 来装饰它。

【讨论】:

    【解决方案2】:

    您可以通过迭代 JPropertyJObject 来从 json 中删除空的 ObjectArray

    class Program
    {
        static void Main(string[] args)
        {
            string json = File.ReadAllText(@"C:\Users\xxx\source\repos\ConsoleApp4\ConsoleApp4\Files\json6.json");
    
            JObject data = JObject.Parse(json);
    
            //Getting all those children that have value are empty from outer json
            var result1 = data.Children<JProperty>()
                 .Where(x => (x.Value.Type == JTokenType.Object) && !x.Value.HasValues)
                .ToList();
    
            //Getting all those children that have value are empty from "question" object
            var result2 = data["question"].Children<JProperty>()
                .Where(x => (x.Value.Type == JTokenType.Object && !x.Value.HasValues) || (x.Value.Type == JTokenType.Array && !x.Value.HasValues))
                .ToList();
    
    
            //Remove all above empty object or arrays
            result1.ForEach(x => x.Remove());
            result2.ForEach(x => x.Remove());
    
            var obj = data.ToObject<JObject>();
    
            Console.WriteLine(obj);
    
            Console.ReadLine();
        }
    }    
    

    输出:

    注意:如果您只想删除整个 json 中的空 lazyLoader 对象,请在上面的代码中使用以下行。

    //Getting "lazyLoader" children that have value are empty from outer json
    var result1 = data.Children<JProperty>()
         .Where(x => x.Value.Type == JTokenType.Object && !x.Value.HasValues && x.Name == "lazyLoader")
         .ToList();
    
    //Getting "lazyLoader" children that have value are empty from "question" object
    var result2 = data["question"].Children<JProperty>()
         .Where(x => (x.Value.Type == JTokenType.Object && !x.Value.HasValues && x.Name == "lazyLoader"))
         .ToList();
    

    输出:

    【讨论】:

      【解决方案3】:

      我知道已经有一个公认的答案,但是基于 a suggestion at EFCore reposanother SO answer it referenced ,我将把这种替代方法留给那些只想摆脱lazyLoader 属性的人

      创建一个CustomContractResolver.cs

      public class CustomContractResolver : DefaultContractResolver
      {
          public static CustomContractResolver Instance { get; } = new CustomContractResolver();
      
          protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
          {
              JsonProperty property = base.CreateProperty(member, memberSerialization);
              if (member.Name == "LazyLoader")
              {
                  property.Ignored = true;
              }
              return property;
          }
      
      }
      

      并修改您的 Startup.csConfigureServices 方法以使用您的 ContractResolver

      services
          .AddMvc(...)
          .AddJsonOptions(options => {
              options.SerializerSettings.ContractResolver = CustomContractResolver.Instance;
          });
      

      【讨论】:

        【解决方案4】:

        在 Entity Framework 中,如果您的对象的一个​​或多个属性使用延迟加载,请使用 GetType().Name 检查其运行时类型名称。例如,对于 Car 类的对象,您会注意到运行时类型实际上是称为 CarProxy 的东西,它是由 Entity Framework 创建的临时内存类型使用反射。这个“假”代理类的基类型是 Car,并具有所有原始 Car 属性,但包括一个名为 LazyLoader 的额外属性,用于可能需要它。

        如果您进一步检查这个“假”CarProxy 类型,您还会看到 Assembly.IsDynamic = true,这表明该类是动态创建的使用反射(见documentation):

        var TheCar = DBContext.Cars.Find(1);
        Console.WriteLine(TheCar.GetType().Assembly.IsDynamic.ToString()); //will echo "true"
        

        幸运的是,Newtonsoft.JsonJsonConvert.SerializeObject() 方法有一个覆盖,它允许我们提供一个基本类型,因此生成的 JSON 不会包含该类型中不存在的属性。因此,要消除 LazyLoader 属性,只需提供对象的 BaseType 作为类型参数即可:

        var TheCar = DBContext.Cars.Find(1);
        var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType);
        

        为确保在序列化时不会出现任何循环引用循环(使用延迟加载时的可能性非常高),请使用以下设置调用序列化程序:

        var TheCar = DBContext.Cars.Find(1);
        var Settings = new Newtonsoft.Json.JsonSerializerSettings
        {
            ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
        };
        var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType, Settings);
        

        注意:当序列化程序遍历对象时,这可能仅在第一级深度有效。如果您提供给序列化程序的对象还有更多延迟加载子属性,则“LazyLoader”属性可能会再次出现。我没有测试过,所以不能肯定。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-09-12
          • 1970-01-01
          • 2021-10-29
          • 1970-01-01
          • 1970-01-01
          • 2018-04-21
          • 1970-01-01
          相关资源
          最近更新 更多