【问题标题】:Avoid even Option fields. Always empty string for String and 0 for Int optional fields甚至避免选项字段。 String 始终为空字符串,Int 可选字段始终为 0
【发布时间】:2015-01-08 07:06:31
【问题描述】:

我有基于 JSON 和 Play Framework 的 scala REST 服务。 JSON 的一些字段是可选的(例如 middleName)。我可以将其标记为选项,例如

 middleName: Option[String]

甚至不要期望它在 JSON 中。但我想避免将来可能出现的应用程序错误并简化生活。如果用户不想提供此信息并且在整个应用程序中没有选项字段(JSON/DB 开销很小),我想将其标记为可预期但为空。

在整个应用程序中避免使用选项字段是个好主意吗?如果 String 字段为空,则它包含一个空字符串,但必须存在于 JSON/DB 中。如果 Int 字段为空,则包含 0 等

提前致谢

【问题讨论】:

  • 当前接受的答案真的回答了这个问题吗?您是否不想知道如何获得 JSON 序列化以将 Option 数据表示为 "field": null 而不是省略它? (“我想……”)我看不出接受的答案以任何方式解决了这个问题。
  • @AmigoNico 我开始了新项目,我只是想知道人们对避免使用 Option 的看法。所有回答的开发人员都写了同样的答案并回答了同样的好。如果有很多例子和一些理论更好的答案,请写。你会被录取
  • 我明白了。我可能误解了您的问题也是关于在转换为 JSON 的对象中使用 Option 的问题。无论如何,我已经更新了我的答案。

标签: json scala rest


【解决方案1】:

我想你会因为失去类型安全而后悔避免使用Option。如果您传递可能为 null 的对象引用,每个接触它们的人都必须记住检查是否为 null,因为没有什么可以强迫他们这样做。不记得是NullPointerException 等待发生。使用Option 强制代码处理没有使用价值的可能性;忘记这样做会导致编译错误:

case class Foo(name: Option[String])
...
if (foo1.name startsWith "/")  // ERROR: no startsWith on Option

我偶尔会在非常本地化的代码中使用空值,我认为要么性能很关键,要么我有很多很多对象并且不想让所有这些 SomeNone 对象占用内存,但我永远不会通过公共 API 泄​​漏空值。使用空值是一种复杂的优化,只有在需要额外警惕以避免灾难的情况下才应该使用它的好处。这种情况很少见。

我不完全确定我了解您对 JSON 的需求,但听起来您可能希望 Option 字段不会从 JSON 文档中消失。在Spray-json 中有一个NullOptions 特性专门用于此。您只需将它混合到您的协议类型中,它就会影响其中定义的所有JsonFormats(如果您愿意,您可以使用其他“不”混合它的协议类型),例如

trait FooJsonProtocol extends DefaultJsonProtocol with NullOptions {
  // your jsonFormats
}

没有NullOptionsOptionNone 的成员被完全省略;有了它,它们就会出现null 值。我认为,如果您显示带有空值的可选字段而不是让它们消失,那么用户会更清楚,但是为了传输效率,您可能希望它们被省略。使用 Spray-json,至少你可以挑选。

我不知道其他 JSON 包是否有类似的选项,但如果由于某种原因您不想使用 Spray-json(顺便说一下,它非常快),也许这会帮助您寻找它现在)。

【讨论】:

    【解决方案2】:

    我认为这取决于您的业务逻辑以及您希望如何使用这些值。

    middleName 的情况下,我假设您主要使用它来以个人方式与用户联系,您只需连接titlefirstNamemiddleNamelastName。因此,无论用户是否指定了值,您都将其视为完全相同。所以我认为使用空字符串而不是 None 可能更可取。

    如果值 0"" 在您的业务逻辑方面是一个有效值,我会选择 Option[String],同样在您有不同行为的情况下,取决于值是否是指定与否。

    x match {
      case 0 => foo
      case _ => bar(_) 
    }
    

    描述性不如

    x match {
      case Some(i) => bar(i)
      case None => foo
    }
    

    【讨论】:

      【解决方案3】:

      这是一个坏主意,因为通常你想以不同的方式处理缺少的东西。如果你传递一个 "" 或 0 的值,这很容易与实际值混淆;您最终可能会发送一封以“亲爱的先生”开头的电子邮件,或者祝他们 35 岁生日快乐,因为时间戳 0 出现在 1970 年 1 月 1 日。如果您在代码和类型系统中区分值和 None ,这迫使您考虑是否实际设置了一个值,如果没有设置,您想做什么。

      也不要盲目地将Options 推到任何地方。如果没有提供值是错误,您应该立即检查并尽快抛出错误,而不是等到应用程序中的很晚,因为这将更难调试 None 的来源。

      【讨论】:

        【解决方案4】:

        它不会让你的“生活更轻松”。如果有的话,它会让事情变得更难,而不是避免应用程序错误会让它们更有可能发生。您的应用程序代码将不得不被if(middleName != "") { doSomething(middleName); }if(age == 0) "Unknown age" else age.toString 之类的检查所感染,并且您将不得不依靠程序员记住以特殊方式处理那些“有点可选”的字段。

        所有这些你都可以使用带有middleName.foreach(doSomething)age.map(_.toString).getOrElse("") 的Option 的一元属性“免费”获得

        【讨论】:

          猜你喜欢
          • 2021-07-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-24
          • 2020-07-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多