【问题标题】:How can I exclude some public properties from being serialized into a JsonResult?如何排除一些公共属性被序列化为 JsonResult?
【发布时间】:2009-09-15 17:38:20
【问题描述】:

我有一个使用 JsonResult 序列化的自定义视图模型。 ViewModel 有一些必须公开的属性,但同时这些属性在生成的 Json 输出中不应该是可见的。

我已经尝试过使用 [NonSerialized] 属性,但这似乎没有任何效果。

有什么简单的方法可以做到这一点吗?还是我必须编写自己的结果类型(在这种情况下我可能不会打扰)?

【问题讨论】:

  • 大部分答案是使用属性或包装器。我只想在序列化过程中简单地排除一些公共属性。我正在寻找@Charlino 建议的 JSON.NET,但没有找到方法。就是这种情况:我有 Error 属性,它只会在发生错误时设置。客户端将首先检查是否显示消息,否则将显示模型属性的其余部分。没有错误时会渲染{...,"Error":null}!

标签: asp.net-mvc json serialization


【解决方案1】:

您可以将[ScriptIgnore] 属性放在不应序列化的成员上。示例见ScriptIgnoreAttribute Class in MSDN

【讨论】:

  • 对于其他人来说,该属性的完整命名空间是 [System.Web.Script.Serialization.ScriptIgnore]
  • 使用常规 MVC 时看起来像 ScriptIgnore return Json() --- 如果使用 Json.NET,则像 JsonIgnore
【解决方案2】:

只需创建一个返回的接口而不是一个类。

public interface IMyViewModel {
   string MyPublicProperty { get; set; }
}

然后创建一个继承接口的类

public class MyViewModel : IMyViewModel {
   public string MyPublicProperty { get; set; }
   public string MyNotSoPublicProperty { get; set; }
}

并在控制器动作中返回接口,而不是类

public JsonResult MyJson(){
    IMyViewModel model = new MyViewModel();
    return Json(model);
}

生成的 JSON 将是

{
    'MyPublicProperty': ''
}

客户端脚本的挑战之一是,如果您要更改类,您不知道是否要破坏客户端实现。如果您在 JSON 中使用接口类型,您就会明白,如果您更改接口,您正在做的事情可能会扼杀客户端实现。如果您要更改不在接口中的内容(因此不会被序列化),它还可以避免您徒劳地重复检查客户端。

此外,很多时候,您的 ViewModel 中可能包含您不一定希望输出到客户端的大型集合或复杂类型。这些可能需要很长时间来序列化或公开根本不属于客户端代码的信息。使用接口可以更透明地了解输出中的内容。

此外,在属性上使用诸如 [ScriptIgnore] 之类的属性仅适用于特定场景(JavaScript 序列化),如果您稍后序列化为 XML,则迫使您面临完全相同的问题。这将不必要地用大量属性乱扔您的视图模型。你真的想要几个在那里?使用接口适用于任何地方,并且不需要在视图模型中添加额外的属性。

【讨论】:

  • 感谢您的回答。这不是一个糟糕的方法,但还有一种更自动化的方法可以确保您的客户端类仍然与服务器端同步:使用 Typescript 作为客户端代码并以编程方式从服务器端对象生成客户端视图模型在编译期间。有关详细信息,请参阅type.litesolutions.net
  • 这种方法是否适用于正在序列化的 List?我不能让它工作......
【解决方案3】:

看看 James Newton-King 的 JSON.NET。它会满足您的需求。

【讨论】:

【解决方案4】:

扩展JavaScriptConverter 类以不包含NonSerializedAttribute 的属性。然后你可以创建一个自定义的ActionResult 使用你的JavaScriptConverter 来序列化对象。

这将创建一个可靠且可测试的类,而无需(重新)生成包装类或使用匿名对象。

【讨论】:

  • 这也是我想到的方法。它确实有效,但由于这只是为了节省每个 Json 请求的几个字节,我希望有一些更简单的东西。很好的方法,但与我的情况相比,工作量太大了。
【解决方案5】:

您可以创建一个包装类,仅在 JsonResult 中公开您想要的那些属性。在下面的示例中,Cow 有 2 个属性 - “Leg”和“Moo”。假设您只想将“Leg”作为属性公开。那么

Dim cw as CowWrapper = New CowWrapper(c)

将返回一个只暴露“Leg”的包装类。如果您只想显示一些属性子集,这对于 DataGridView 之类的东西也很有用。

公共课牛

Public ReadOnly Property Leg() as String

   get

      return "leg"

   end get

结束属性

Public ReadOnly Property Moo() as String

   get

      return "moo"

   end get

结束属性

结束课程

公共类 CowWrapper

Private m_cow as Cow = Nothing

Public Sub New(ByVal cow as Cow)

   m_cow = cow

end Sub


    m_cow = cow

Public ReadOnly Property Leg() as String

   get

      return m_cow.Leg()

   end get

结束属性

结束课程

【讨论】:

  • 这行得通,但代价是必须复制很多属性,所以这并不是我真正想要的。
  • 费用是多少?编程?给定类文件作为输入,编写一个简单的实用程序来构造这些类应该很简单,或者编写一个可以使用自省动态构造属性的类。运行?额外参考的成本可以忽略不计。
  • 我没有意识到你的意思是在运行时通过反射构造这样的类型。这当然会涵盖我所有的自定义视图模型类型,而无需任何额外的编码。一种有效的方法,但与使用现成工具(即 Json.Net)相比过于复杂
  • 太棒了! (即有现成的工具)。我应该写“它应该是可能的”而不是“它应该很简单”......因为我自己实际上对内省知之甚少。在使用 LINQ to SQL 类填充 DataGridView 时,我使用了包装类(硬编码,不使用自省)。通常我不想显示表格中的所有字段,所以包装器会处理这个问题。
【解决方案6】:

不完全是您正在寻找的答案,但您可以使用以下代码和匿名类欺骗Json()

MyModel model = ...;
return Json(new MyModel {model.Prop1, model.Prop2});

【讨论】:

  • 我知道我可以使用匿名类型。但这使得单元测试结果变得更加困难。这意味着我要么必须解析序列化结果,要么使用反射。
  • 单元测试 JsonResults 很难开始。你以前是怎么做的?您似乎无法检索驱动 JsonResult 的底层模型。
  • 完全没问题。 JsonResult.Data 属性包含要序列化的对象。由于我使用的是自定义视图模型而不是匿名对象类型,因此我可以简单地抓取该对象并测试它的属性。
  • 好吧,虽然很痛苦,但您总是可以创建一个新版本的类并简单地复制您想要的那些属性。我用这种方法编辑了我的帖子。
  • @Adrian:谢谢,我不知道 Data 指的是序列化对象。
【解决方案7】:

我需要 ASP.NET Core 6.x 的答案,但找不到。

我终于找到了答案,它是:

[System.Text.Json.Serialization.JsonIgnore]

这是一个类中的示例

class Sample{
    // Item will not be serialized
    [System.Text.Json.Serialization.JsonIgnore]
    String Item{get;set;}
    // Count will be serialized
    int Count{get;set;}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-06
    • 2018-03-06
    • 2010-10-09
    • 2018-08-03
    • 1970-01-01
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多