dotnet核心Web API JSON响应包含不必要的元素

时间:2018-10-22 05:35:54

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

我懒于将表数据加载到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));

4 个答案:

答案 0 :(得分:2)

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

无需实施新模型的更快方法是使用匿名对象,例如:

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

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

答案 1 :(得分:1)

您可以像这样在ObjectArray进行迭代,从而从json中删除空的JPropertyJObject

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

输出:

enter image description here

注意::如果您想在整个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();

输出:

enter image description here

答案 2 :(得分:1)

我知道已经有一个可接受的答案,但是基于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.cs ConfigureServices方法以使用ContractResolver

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

答案 3 :(得分:0)

在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.Json JsonConvert.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”属性可能会再次出现。我还没有测试过,所以不能确定。