2
\$\begingroup\$

I have the below function which populates different time-related case classes based on what is present in the JSON input.

def parseTime(timeJson: JsValue) = {
   val day = (timeJson \ "day").getOrElse(JsString("")).as[JsString].value.toInt
   val week = (timeJson \ "month").getOrElse(JsString("")).as[JsString].value.toInt
   val month = (timeJson \ "month").getOrElse(JsString("")).as[JsString].value.toInt
   val year = (timeJson \ "year").getOrElse(JsString("")).as[JsString].value.toInt
   if (day != "") {
     YearMonthDay(
       year = year,
       month = month,
       day = day)
   }
   else if (month != "") {
     YearMonth(
       year = year,
       month = month
     )
   }
   else if (week != "") {
     YearWeek(
       year = year,
       week = week
     )
   }
   else {
     Year(year)
   }
 }

Is there a cleaner way to check whether specific fields are present in my tuple of 4 values (day, week, month, year)

\$\endgroup\$
3
  • \$\begingroup\$ The posted code lacks the import statements necessary for it to compile. \$\endgroup\$ Commented Sep 17, 2020 at 2:50
  • \$\begingroup\$ There is scala.util.parsing.json.JSON as well as Circe; you seem to be reinventing-the-wheel: tag accordingly. \$\endgroup\$ Commented Sep 17, 2020 at 6:08
  • \$\begingroup\$ Not related to the json, but you can remove most of those braces. != "" could be .nonEmpty \$\endgroup\$ Commented Sep 21, 2020 at 18:59

1 Answer 1

1
\$\begingroup\$

Let this be the definition of your Date types:

sealed trait Date {
  def year: Int
}

case class Year(year: Int) extends Date
case class YearMonth(year: Int, month: Int) extends Date
case class YearWeek(year: Int, week: Int) extends Date
case class YearMonthDay(year: Int, month: Int, day: Int) extends Date

In your question there are problems when value is not defined, or when it is empty string, or when value is present, but it is not a number. So you can use common function readInt to read optional integer from JSON. In case if value is not present or if value is empty or non-integer, the result of function will be scala.util.Failure.

  def parseDate(dateJson: JsValue): Try[Date] = {
    def readInt(fieldName: String): Try[Int] =
      (dateJson \ fieldName).asOpt[String].filter(_.nonEmpty).map(x => Try(x.toInt))
        .getOrElse(Failure(new IllegalArgumentException(s"$fieldName is empty")))

    val maybeDay: Try[Int] = readInt("day")
    val maybeWeek: Try[Int] = readInt("week")
    val maybeMonth: Try[Int] = readInt("month")
    val maybeYear: Try[Int] = readInt("year")

    maybeYear.map { year: Int =>
      maybeMonth match {
        case Success(month) => maybeDay match {
          case Success(day) => YearMonthDay(year, month, day)
          case _ => YearMonth(year, month)
        }
        case _ => maybeWeek match {
          case Success(week) => YearWeek(year, week)
          case _ => Year(year)
        }
      }
    }
  }

My tests:

val date1 = JsObject(Map("year" -> JsString("2001"), "month" -> JsString("11"), "day" -> JsString("30")))
println(parseDate(date1)) // => Success(YearMonthDay(2001,11,30))
val date2 = JsObject(Map("year" -> JsString("2001"), "month" -> JsString("11")))
println(parseDate(date2)) // => Success(YearMonth(2001,11))
val date3 = JsObject(Map("year" -> JsString("2001"), "week" -> JsString("51")))
println(parseDate(date3)) // => Success(YearWeek(2001,51))
val date4 = JsObject(Map("year" -> JsString("")))
println(parseDate(date4)) // => Failure(java.lang.IllegalArgumentException: year is empty)

Sorry for my English

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.