Getting better error messages from Play JSON API

10,181

yes using JsValue.as[T] isn't robust in this case:

res0 \ "p" returns JsUndefined which is then applied to an implicit Reads[String] and it says "hey that's not a String"... That's true but not what you expect ;)

the better way IMHO uses Reads on JsPath directly:

scala> ((__ \ "p").read[String]).reads(res0)
res7: play.api.libs.json.JsResult[String] = JsError(List((/p,List(ValidationError(validate.error.missing-path,WrappedArray())))))

scala> ((__ \ "k" \ "m").read[String]).reads(res0)
res8: play.api.libs.json.JsResult[String] = JsError(List((/k/m,List(ValidationError(validate.error.expected.jsstring,WrappedArray())))))

Here you have a better message about missing-path. It even notifies the missing path in first param in JsError.

Concerning messages as nice strings, you can use a local message to map the key. BTW, those Json errors message are not quite standardized with respect to other error messages in Play such as Forms. I think we will improve it in a future release of Play.

Finally, when you want to send Json validation errors to the client, there is a helper function in JsError to use with JsResult.recoverTotal

scala> ((__ \ "k" \ "m").read[String]).reads(res0).recoverTotal( e => JsError.toFlatJson(e) )
res11: java.io.Serializable = {"obj.k.m":[{"msg":"validate.error.expected.jsstring","args":[]}]}

This is just a raw version of this kind of function. If you need an another format, I advise to look at toFlatJson implementation and write your own.

Have fun ;)

Share:
10,181
missingfaktor
Author by

missingfaktor

I am no longer active on this site, but if my posts help you and/or you need further help, do let me know on Twitter at @missingfaktor. I will try my best to respond! Note: This profile description was written for StackOverflow, and was copied to all other StackExchange sites as-is.

Updated on June 13, 2022

Comments

  • missingfaktor
    missingfaktor almost 2 years

    Examples of the kind of error messages produced by Play JSON API:

    scala> import play.api.libs.json._
    import play.api.libs.json._
    
    scala> Json.obj("k" -> Json.obj("m" -> 7))
    res0: play.api.libs.json.JsObject = {"k":{"m":7}}
    
    scala> (res0 \ "p").as[String]
    play.api.libs.json.JsResultException: JsResultException(errors:List((,List(ValidationError(validate.error.expected.jsstring,WrappedArray())))))
        at play.api.libs.json.JsValue$$anonfun$2.apply(JsValue.scala:67)
        at play.api.libs.json.JsValue$$anonfun$2.apply(JsValue.scala:67)
        at play.api.libs.json.JsResult$class.fold(JsResult.scala:69)
        at play.api.libs.json.JsError.fold(JsResult.scala:10)
        at play.api.libs.json.JsValue$class.as(JsValue.scala:65)
        at play.api.libs.json.JsUndefined.as(JsValue.scala:98)
        at .<init>(<console>:12)
        at .<clinit>(<console>)
        at .<init>(<console>:7)
        // gazillion lines more
    
    scala> (res0 \ "k" \ "m").as[String]
    play.api.libs.json.JsResultException: JsResultException(errors:List((,List(ValidationError(validate.error.expected.jsstring,WrappedArray())))))
        at play.api.libs.json.JsValue$$anonfun$2.apply(JsValue.scala:67)
        at play.api.libs.json.JsValue$$anonfun$2.apply(JsValue.scala:67)
        at play.api.libs.json.JsResult$class.fold(JsResult.scala:69)
        at play.api.libs.json.JsError.fold(JsResult.scala:10)
        at play.api.libs.json.JsValue$class.as(JsValue.scala:65)
        at play.api.libs.json.JsNumber.as(JsValue.scala:108)
        at .<init>(<console>:12)
        at .<clinit>(<console>)
        at .<init>(<console>:7)
        // gazillion lines more
    

    Is there a way to get better error messages out of this API? For example, the error messages for above two cases could look like No value found at JsPath \ "p", Value found at JsPath \ "k" \ "m" cannot be read as type String etc.