Niestety nie znam żadnej naprawdę dobrej biblioteki, która zrobi to w XML (chociaż warto przyjrzeć się Anti-Xml). Ale są takie biblioteki dla Json i jest to PlayJson z luzem json można zrobić w zasadzie wszystko, co można zrobić z Ruby JsBuilder, z wyjątkiem jakiejś różnicy paradygmatu:
Scala stara się być tak funkcjonalne, jak to możliwe i wiele wiele biblioteki działają z niepoprawnymi strukturami danych. Zagraj w json nie jest wyjątkiem. Oznacza to, że nie można zmienić wartości w głębi drzewa json, należy zrekonstruować cały obiekt json. Jest to język statycznie napisany. Co jest świetne, ponieważ kompilator sprawdza poprawność wszystkich podpisów typów, z tym że musimy dostarczyć te podpisy.
Oto przykładowy kod:
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
case class Attachment(fileName: String, url: String)
case class Author(name: String, email: String, url: String)
case class Comment(content: String, created_at: DateTime)
case class Post(author: Author, content: String, attachments: List[Attachment], comments: List[Comment], created_at: DateTime, updated_at: DateTime)
object Main {
import play.api.libs.json._
import play.api.libs.functional.syntax._
val isAdmin = true
def calculateVisits(post: Post) = 35
implicit val jodaTimeWrites: Writes[DateTime] = new Writes[DateTime] {
def writes(c: DateTime): JsValue = {
Json.toJson(c.toString(DateTimeFormat.fullDateTime()))
}
}
implicit val attachmentFormat = Json.format[Attachment]
implicit val authorWrites: Writes[Author] = (
(__ \ "name").write[String] and
(__ \ "email").write[String] and
(__ \ "url").write[String]) { unlift(Author.unapply) }
implicit val commentWrites: Writes[Comment] = (
(__ \ "content").write[String] and
(__ \ "created_at").write[DateTime]) { unlift(Comment.unapply) }
implicit val postWrites: Writes[Post] = (
(__ \ "content").write[String] and
(__ \ "created_at").write[DateTime] and
(__ \ "updated_at").write[DateTime] and
(__ \ "author").write[Author] and
(__ \ "visitors").write[Option[Int]] and
(__ \ "comments").write[List[Comment]] and
(__ \ "attachments").write[List[Attachment]]) { post: Post =>
(
post.content,
post.created_at,
post.updated_at,
post.author,
if (isAdmin) Some(calculateVisits(post)) else None,
post.comments,
post.attachments)
}
def main(args: Array[String]): Unit = {
val post = Post(
Author("David H.", "[email protected]", "http://example.com/users/1-david.json"),
"<p>This is <i>serious</i> monkey business</p>",
List(
Attachment("forecast.xls", "http://example.com/downloads/forecast.xls"),
Attachment("presentation.pdf", "http://example.com/downloads/presentation.pdf")),
List(
Comment("Hello everyone!", new DateTime()),
Comment("To you my good sir!", new DateTime())),
new DateTime(),
new DateTime())
Console println (Json prettyPrint (Json toJson post))
}
}
Zawiadomienie attachmentFormat
- to są generowane przez makr Scala, należy dostosować do definicji klasy przypadek. W moim projekcie nigdy nie napisałem ani jednego podręcznika do kompilatora formatu, który generuje dla mnie wszystkie formaty! Ale mogę, jeśli muszę. Dobrym przykładem jest jodaTimeWrites
- domyślnie jodaTimeFormat wygeneruje długą wartość, która jest bardziej odpowiednia do przetwarzania maszynowego, ale nadpisałem ją moim własnym niejawnym formatem, aby dopasować próbkę ruby.
Powyższy kod tworzy następujące wyjścia:
{
"content" : "<p>This is <i>serious</i> monkey business</p>",
"created_at" : "Friday, July 5, 2013 4:19:42 PM +03:00",
"updated_at" : "Friday, July 5, 2013 4:19:42 PM +03:00",
"author" : {
"name" : "David H.",
"email" : "[email protected]",
"url" : "http://example.com/users/1-david.json"
},
"visitors" : 35,
"comments" : [ {
"content" : "Hello everyone!",
"created_at" : "Friday, July 5, 2013 4:19:42 PM +03:00"
}, {
"content" : "To you my good sir!",
"created_at" : "Friday, July 5, 2013 4:19:42 PM +03:00"
} ],
"attachments" : [ {
"fileName" : "forecast.xls",
"url" : "http://example.com/downloads/forecast.xls"
}, {
"fileName" : "presentation.pdf",
"url" : "http://example.com/downloads/presentation.pdf"
} ]
}
Teraz zamiast 21 linii kodu ruby Mam 33 wierszy Scala jakoś bardziej skomplikowanych odwzorowań (bez klas przypadku). Po co pisać więcej? Ponieważ teraz nie jestem pewien, że gdy minie Comment
zamiast Attachment
, otrzymam błąd kompilatora, lub gdy mój kolega zmieni się przez pomyłkę joda.time.DateFormat na java.util.DateFormat, otrzyma błąd zamiast bełkotu .