Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

JSON transform with Play

61 views
Skip to first unread message

panshin

unread,
Nov 20, 2015, 11:04:51 AM11/20/15
to Scala в России
Доброго дня, любители Scala!

Помогите советом или делом, как обработать такой вот json:

{
"date": "2015-01-01",
"id": 100,
"movies":[
{
"id": 1,
"length": 131,
"timestamp": 1447837200
},
{
"id": 2,
"length": 131,
"timestamp": 1447840800
}
]
}

Массив movies содержит сущности Movie, для которых, в принципе, легко и красиво написать Json.Reads. Но задачу немного усложняет то, что эти сущности неполноценные, в каждой не хватает данных из корня json - id и date.

Логичным кажется сначала совершить преобразование исходного json в такую форму:

[
{
"id": 1,
"length": 131,
"timestamp": "09:00",
"parent_id": 100,
"date": "2015-01-01"
},
{
"id": 2,
"length": 131,
"timestamp": "10:00",
"parent_id": 100,
"date": "2015-01-01"
}
]

+ на это все надо навесить валидации - date - правильный YYY-mm-DD, id - входит в условный список IDS.

Очень надеюсь на полезные советы

mai...@chuwy.me

unread,
Nov 22, 2015, 7:57:04 AM11/22/15
to Scala в России
Привет, Глеб.

Не совсем уверен как ты хочешь обработать этот JSON, но издалека, я бы предположил подобную структуру:

case class Bunch(id: Int, date: String, movies: List[Movie])
case class Movie(id: Int, length: Int, timestamp: Int)

И уже от неё делать все прочие преобразования.

Глеб Паньшин

unread,
Nov 22, 2015, 10:59:34 AM11/22/15
to Scala в России
спасибо за ответ!

Да, подобную систему я уже в голове и вижу, но конкретная реализация на Play.json красиво не выходит.


Решение которое предложили мягко говоря выглядит ужасно.



воскресенье, 22 ноября 2015 г., 15:57:04 UTC+3 пользователь mai...@chuwy.me написал:

Alexander Tchitchigin

unread,
Nov 22, 2015, 3:03:32 PM11/22/15
to scala-...@googlegroups.com
Я бы сказал, что если парсинг монадический, то можно добавить State monad (или Reader monad), в ней запомнить parent_id и date когда мы их распарсили, почле чего просто вычитывать из состояния и добавлять при разборе отдельных фильмов...

Предварительная трансформация всего JSON кажется избыточной. Уж лучше тогда SAX-style парсинг использовать...


--
--
Страница рассылки: http://groups.google.com/group/scala-russian

---
Вы получили это сообщение, поскольку подписаны на группу "Scala в России".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес scala-russia...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.



--
С уважением,
Александр.

mai...@chuwy.me

unread,
Nov 23, 2015, 12:07:34 AM11/23/15
to Scala в России
Если я правильно понимаю, то здесь на входе и на выходе JSON.
Я бы это спарсил в ту структуру, которую я предложил. Не работал с Play JSON, но в json4s это выглядело бы так:

case class ResultMovie(id: Int, length: Int, timestamp: String, parent_id: Int, date: Date)
val bunchOfMovies = parse(text).extractOpt[Bunch]
def processMovie(parentId: Int, date: Date, movie: Movie): ResultMovie = 
  ResultMovie(movie.id, movie.length, getHhMm(movie.timestamp), parentId, date)
val listOfMovies: Option[List[ResultMovie]] = bunchOfMovies.map { bunch =>
bunch.movies.map { movie =>
processMovie(bunch.id, processDate(bunch.date), movie)
}
}
pretty(listOfMovies}


То есть уже имея нормальные Scala-объекты производил бы преобразования дат/таймштампов. Мне кажется, это выглядит более коротким, понятным и типобезопасным.
Да в общем-то и твоё решение не такое уж ужасное.

А Б

unread,
Nov 23, 2015, 5:25:35 AM11/23/15
to scala-...@googlegroups.com
Добрый день! Я в таких случаях делаю как-то так:

import play.api.libs.json._

case class MyMovie(bunch_id:Int, bunch_date:String, id: Int, length: Int, timestamp: Int)

case class Movie(id: Int, length: Int, timestamp: Int) {
  def toMyMovie(bunch_id:Int, bunch_date:String) = MyMovie(bunch_id, bunch_date, id, length, timestamp)
}
implicit val Movie_reader = Json.reads[Movie]

case class Bunch(id: Int, date: String, movies: List[Movie]) {
  lazy val my_movies = movies.map(m => m.toMyMovie(id, date))
}

implicit val Bunch_reader = Json.reads[Bunch]

val json = """{
  "date": "2015-01-01",
  "id": 100,
  "movies":[
    {
      "id": 1,
      "length": 131,
      "timestamp": 1447837200
    },
    {
      "id": 2,
      "length": 131,
      "timestamp": 1447840800
    }
  ]
}"""

val jvalue = Json.parse(json)

jvalue.validate[Bunch] match {
  case JsSuccess(bunch, _) =>
    println(bunch.my_movies.mkString(", "))
  case JsError(error) => 
    println(error)
}

Код точно актуален для Play 2.2.x, мб и для более поздних версий.


23 ноября 2015 г., 8:07 пользователь <mai...@chuwy.me> написал:

--

Глеб Паньшин

unread,
Nov 26, 2015, 9:34:30 AM11/26/15
to Scala в России
Всем большое спасибо за помощь!

Вариант с использованием вспомогательных классов был у меня, но он мне казался каким-то не очень красивым, JSON трансформы изначально казались изящнее. Но туда оказалось намного труднее прикрутить "много" валидации.



понедельник, 23 ноября 2015 г., 13:25:35 UTC+3 пользователь A B написал:
Reply all
Reply to author
Forward
0 new messages