0
votes

I have scala REST service based on JSON and Play Framework. Some of the fields of the JSON are optional (e.g. middleName). I can mark it Option e.g.

 middleName: Option[String]

and even don't expect it in JSON. But I would like to avoid possible app errors in the future and simplify life. I would like to mark it as expectable but empty if user don't want to provide this info and have no Option fields throughout entire application (JSON/DB overhead is minor).

Is it good idea to avoid Option fields throughout the application? If the String field is empty, it contains an empty string but manadatory present in JSON/DB. If the Int field is empty it contains 0 etc

Thanks in advance

4
Does the currently accepted answer really answer the question? Did you not want to know how you could get JSON serialization to represent Option data as "field": null rather than omitting it? ("I would like to ...") I don't see that the accepted answer addresses this in any way.AmigoNico
@AmigoNico I started new project and I simply wanted to know what people think about avoiding Option. ALL developers who answered wrote the same and answered the same good. If have better answers with lot examples and some theory please write. You will be acceptedAlex
I see. I may have misunderstood your question to also be about the use of Option in objects converted to/from JSON. In any case I've updated my answer.AmigoNico

4 Answers

2
votes

I think you would regret avoiding Option because of the loss of type safety. If you go passing around potentially null object references, everyone who touches them has to remember to check for null because there is nothing that forces them to do so. Failure to remember is a NullPointerException waiting to happen. The use of Option forces code to deal with the possibility that there is no value to work with; forgetting to do so will cause a compilation error:

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

I very occasionally do use nulls in a very localized bit of code where I think either performance is critical or I have many, many objects and don't want to have all of those Some and None objects taking up memory, but I would never leak the null out across a public API. Using nulls is a complicating optimization that should only be used where the extra vigilance required to avoid catastrophe is justified by the benefit. Such cases are rare.

I am not entirely sure I understand what your needs are with regard to JSON, but it sounds like you might like to have Option fields not disappear from JSON documents. In Spray-json there is a NullOptions trait specifically for this. You simply mix it into your protocol type and it affects all of the JsonFormats defined within (you can have other protocol types that do "not" mix it in if you like), e.g.

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

Without NullOptions, Option members with value None are omitted altogether; with it, they appear with null values. I think that it is clearer for users if you show the optional fields with null values rather than having them disappear, but for transmission efficiency you might want them omitted. With Spray-json, at least, you can pick.

I don't know whether other JSON packages have a similar option, but perhaps that will help you look for it if for some reason you don't want to use Spray-json (which, by the way, is very fast now).

1
votes

I think that would depend on your business logic and how you want to use these values.

In the case of the middleName I am assuming you are using it primarily to address the user in a personal manner and you just concatenate title, firstName, middleName and lastName. So you treat the value exactly the same whether the user has specified it or not. So I think using an empty String instead of None might be preferable.

In the case of values where 0 or the "" is a valid value in terms of your business logic I would go with the Option[String], also in cases where you have different behaviours depending on whether the value is specified or not.

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

is less descriptive than

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

It's a bad idea, because normally you want to handle the absence of something differently. If you pass a value of "" or 0 around, this can very easily be confused with a real value; you might end up sending an email that starts "Dear Mr ," or wishing them Happy 35th Birthday because the timestamp 0 comes out as 1st January 1970. If you keep a distinction between a value and None in code and in the type system, this forces you to think about whether a value is actually set and what you want to do if it isn't.

Don't blindly just push Options everywhere though, either. If it's an error for a value to not be supplied, you should check that immediately and throw an error as soon as possible, not wait until much later in your application when it will be harder to debug where that None came from.

1
votes

It won't make your "life easier". If anything, it will make it harder, and instead of avoiding app errors will make them more likely. Your app code will have to be infested with checks like if(middleName != "") { doSomething(middleName); } or if(age == 0) "Unknown age" else age.toString, and you will have to rely on the programmer remembering to handle those "kinda-optional" fields in a special way.

All of this you could get "for free" using the monadic properties of Option with middleName.foreach(doSomething) or age.map(_.toString).getOrElse("")