Oscar Godson Oscar Godson - 3 months ago 10
Scala Question

Add key/value to existing JsValue in Scala

I'm new to Scala and I'm trying to figure out how I would add a key/value to a JsValue. In JavaScript you might do something like

SomeObject.newKey = newValue
. Here's my real use case:

def doAccountUpdate(account: db.models.Account, config: JsObject, job: JsValue) : JsValue = {
val passwordParam = (job \ "params" \ "password")
if (passwordParam.toOption.isDefined) {
val password = (job \ "params" \ "password").get.as[String]
val encrypted_password = Auth.passwordEncrypt(password)
// ADD `encrypted_password`'s value TO `job \ params` so its `job \ params \ encryptedPassword`
}

Account.update(account, config, job)
}


Account.update
actually updates the DB so in my
doAccountUpdate
i'm prepping the data. One of those items I need to prep is password by encrypting it before passing it to
Account.update
. How do I do this?

EDIT

To explain why the question this is marked a duplicate of doesn't work for me is this is their example:

jsonObject.as[JsObject] + ("c" -> Json.toJson(3))


If I duplicate that in my code it would look like this:

updateJob = job.as[JsObject] + ("encryptedPassword" -> Json.toJson(encrypted_password))


but that outputs:

{
"id":"...",
"params": {
"password":"Programmer2!"
},
"encryptedPassword":"$2a$10$EHnJHXh1sTORxliPocWfDuHnlRzu1YwG.kyBee.u85apCXTuLij.y"
}


Note how
encryptedPassword
isn't in
params

Answer

As @danielnixon suggests, you can use JSON transformers for this. Here is an example:

val encryptPassword: Reads[JsObject] = (__ \ "params").json.update(
  (__ \ "password").read[String].flatMap(pw =>
    (__ \ "encryptedPassword").json.put(JsString(Auth.passwordEncrypt(pw)))
  )
)

This says we will:

  1. update the params object
  2. read the existing password
  3. put a new encryptedPassword branch in the params object

When you apply that with job.transform(encryptPassword) you'll get a JsResult that, if the params object exists and has a password field with a string value, will be JsSuccess - otherwise you'll get JsError. You can handle that with something like the following:

def doAccountUpdate(account: db.models.Account, config: JsObject, job: JsValue) : JsValue = {
  job.transform(encryptPassword) match {
    case JsSuccess(newJob, _) => Account.update(account, config, newJob)
    case JsError(errors) => sys.error("No password supplied!")
  }
}
Comments