0

I'm trying to parse a json string of search results of the following form:

"""
{
  "metadata": [
    "blah",
    "unimportant"
  ],
  "result": [
    {
      "type": "one",
      "title": "this",
      "weird": "attribute unique to this type",
      "other": 7
    },
    {
      "type": "two",
      "title": "that",
      "another_weird": "a different attribute unique to this second type",
      "another": "you get the idea"
    },
    {
      "type": "one",
      "title": "back to this type, which has the same fields as the other element of this type",
      "weird": "yep",
      "other": 8
    }
    ...
  ]
}
"""

There is a known, fixed number of result element types (given by the type field), each of which correspond to a unique schema. For a given search request, there can be any number of each type returned in any order (up to some fixed total).

Writing out the case classes and Reads implicits for each type is easy enough, but my question is around the best way to handle the variability in the result sequence... pattern matching seems the obvious choice, but I'm just not sure where or how with the play framework. Thanks in advance!

EDIT: Per the suggestion in the comments, I gave it a go by attempting to read the result sequence as subtypes of a common base trait, but that didn't quite work. The following compiles, but doesn't parse the example json correctly. Any additional suggestions are welcome.

sealed trait Parent {def title: String}
case class One(`type`: String, title: String, weird: String, other: Int) extends Parent
case class Two(`type`: String, title: String, another_weird: String, another: String) extends Parent

case class SearchResponse(result: Seq[Parent], metadata: Seq[String])

implicit val oneReader = Json.reads[One]
implicit val twoReader = Json.reads[Two]
implicit val parentReader = Json.reads[Parent]

implicit val searchReader = (
  (__ \ "result").read[Seq[Parent]] and (__ \ "metadata").read[Seq[String]]
)(SearchResponse.apply _)

val searchResult = Json.fromJson[SearchResponse](json)
print(searchResult)
5
  • 1
    Did you try sealed trait Parent { def title: String } case class One(title: String, weird: String, other: Int) extends Parent case class Two(title: String, another_weird: String, another: String) extends Parent ? Commented Feb 10, 2023 at 0:40
  • playframework.com/documentation/2.8.x/ScalaJson Commented Feb 10, 2023 at 0:57
  • Yep, I did -- I guess my question is, given result is an arbitrary sequence of those cases, how should that be handled in the Reads implicit? Can I just read it as Seq[Parent]? Commented Feb 10, 2023 at 1:02
  • 1
    Yeah, Seq[Parent] sounds reasonable Commented Feb 10, 2023 at 1:05
  • Feel free to come back with your code if you have further questions Commented Feb 10, 2023 at 1:09

1 Answer 1

1

Define implicit JsonConfiguration

import play.api.libs.json._

sealed trait Parent {
  def title: String
}
case class One(title: String, weird: String, other: Int) extends Parent
case class Two(title: String, another_weird: String, another: String) extends Parent

case class SearchResponse(result: Seq[Parent], metadata: Seq[String])

implicit val cfg = JsonConfiguration(
  discriminator = "type",
  typeNaming = JsonNaming(_.toLowerCase)
)

implicit val oneReader    = Json.reads[One]
implicit val twoReader    = Json.reads[Two]
implicit val parentReader = Json.reads[Parent]
implicit val searchReader = Json.reads[SearchResponse]

val searchResult = Json.fromJson[SearchResponse](json)
println(searchResult)
// JsSuccess(SearchResponse(List(One(this,attribute unique to this type,7), Two(that,a different attribute unique to this second type,you get the idea), One(back to this type, which has the same fields as the other element of this type,yep,8)),List(blah, unimportant)),)

https://www.playframework.com/documentation/2.8.x/ScalaJsonAutomated#Custom-Naming-Strategies

Sign up to request clarification or add additional context in comments.

2 Comments

@abw Welcome. Just in case, if there were no such mechanism as JsonConfiguration in play-json we would just have to define a custom reader for Parent. For case classes (One, Two, SearchResponse) standard codecs would be ok.
Interesting... I may try that as well just for learning purposes, appreciate it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.