2013-06-27 25 views
5

Tôi đang sử dụng Scalatra, nhưng câu hỏi này phải hợp lệ đối với bất kỳ chương trình Scala nào. Tôi đến từ một nền Ruby on Rails. Đơn giản chỉ cần đặt, sử dụng hệ thống khuôn mẫu như XML Builder hoặc jsonbuilder (https://github.com/rails/jbuilder), tôi đã kiểm soát hoàn toàn vào những gì JSON tôi hoặc đầu ra XML trong một API RESTful sẽ bằng cách tạo ra một khuôn mẫu như sau:Scala sử dụng JSON và/hoặc mẫu XML

Jbuilder.encode do |json| 
    json.content format_content(@message.content) 
    json.(@message, :created_at, :updated_at) 

    json.author do 
    json.name @message.creator.name.familiar 
    json.email_address @message.creator.email_address_with_name 
    json.url url_for(@message.creator, format: :json) 
    end 

    if current_user.admin? 
    json.visitors calculate_visitors(@message) 
    end 

    json.comments @message.comments, :content, :created_at 

    json.attachments @message.attachments do |attachment| 
    json.filename attachment.filename 
    json.url url_for(attachment) 
    end 
end 

Các lý tưởng ở đây là, tôi đặt cùng một đối tượng @message với bất kỳ logic nào được yêu cầu trong một controller + action. Điều đó được chuyển đến một mẫu có logic như if current_user.admin? bao gồm một số nội dung, nếu không thì không.

Công cụ tương đương có sẵn trong Scala hoặc Scalatra để làm điều gì đó tương tự? Tôi biết một serializer sẽ cho phép tôi ghi đè JSON hoặc XML được tạo từ một mô hình cụ thể, nhưng đó cũng là điều tương tự trong Ruby (sửa tôi nếu tôi sai) như ghi đè as_json hoặc as_xml. Tuy nhiên, đôi khi các mẫu phức tạp hơn nhiều và bao gồm nhiều mô hình, cấu trúc cụ thể của dữ liệu, thứ tự cụ thể của dữ liệu, v.v. Đây là sự linh hoạt mà tôi cần. Có một công cụ cho phép tạo khuôn mẫu như vậy trong môi trường Scala/Scalatra không?

Trả lời

4

tiếc là tôi không biết bất kỳ thư viện thực sự tốt để làm điều này trong XML (mặc dù Anti-Xml là giá trị tham gia). Nhưng cũng có những thư viện đó cho Json và đó là PlayJson Với vở kịch json bạn có thể làm về cơ bản tất cả mọi thứ bạn có thể làm với JsBuilder Ruby, ngoại trừ một số khác biệt mô hình:

  1. Scala cố gắng để được như chức năng càng tốt và nhiều nhiều thư viện hoạt động với cấu trúc dữ liệu không thể thay đổi. Chơi json không phải là ngoại lệ. Điều này có nghĩa là bạn không thể chỉ cần thay đổi một số giá trị sâu trong cây json, bạn cần phải tạo lại toàn bộ đối tượng json

  2. Scala là một ngôn ngữ được nhập tĩnh. Điều này là tuyệt vời, bởi vì trình biên dịch kiểm tra tất cả các chữ ký kiểu cho tính chính xác, ngoại trừ chúng ta phải cung cấp các chữ ký đó.

Dưới đây là mã ví dụ:

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)) 
    } 

} 

Thông báo các attachmentFormat - nó được tạo ra bởi các macro scala để được xuất hiện với định nghĩa ca bệnh lớp. Trong dự án của tôi, tôi chưa từng viết một trình biên dịch ghi đè định dạng thủ công nào tạo ra tất cả các định dạng cho tôi! Nhưng tôi có thể Nếu tôi cần. Ví dụ tốt là jodaTimeWrites - mặc định jodaTimeFormat sẽ tạo ra giá trị lâu dài phù hợp hơn cho việc xử lý máy, nhưng tôi đã ghi đè nó bằng định dạng ẩn của riêng tôi để phù hợp với mẫu của ruby.

Đoạn mã trên tạo ra sau đầu ra:

{ 
    "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" 
    } ] 
} 

Bây giờ thay vì 21 dòng mã ruby ​​tôi có 33 dòng của scala bằng cách nào đó ánh xạ phức tạp hơn (không có trường hợp lớp). Tại sao lại nhập nhiều hơn? Bởi vì bây giờ tôi đã chết chắc chắn rằng khi tôi vượt qua Comment thay vì Attachment tôi sẽ nhận được lỗi biên dịch, hoặc khi đồng nghiệp của tôi sẽ thay đổi do nhầm lẫn joda.time.DateFormat để java.util.DateFormat anh ta sẽ nhận được lỗi thay vì một số sai ngữ pháp .

0

Dưới đây là một phác thảo của một câu trả lời bằng Scala tích hợp trong literals XML và tuyệt vời thư viện argonaut.io JSON serialization:

// dependencies for use with scala 2.10.2: 
// "io.argonaut" %% "argonaut" % "6.0-RC3" 
// "org.scalaz" %% "scalaz-core" % "7.0.1" 

import scalaz._, Scalaz._ 
import argonaut._, Argonaut._ 

object ArgonautJsonExample extends App { 

    case class File(name: String, url: String = "http://my.dummy.url") 
    case class Message(name: String, isAdmin: Boolean, attachmentOpt: Option[File]) 
    val message = 
    new Message(
     "Erik", 
     true, 
     Some(File("strawberry.png")) 
    ) 

    val json: Json = 
    ("name" := message.name) ->: 
    ("filename" :=? message.attachmentOpt.map(_.name)) ->?: 
    ("url" :=? message.attachmentOpt.map(_.url)) ->?: 
    jEmptyObject 


    val myXml = 
    <myObject> 
     <author> 
     <name>{message.name}</name> 
     </author> 
     { 
     message.attachmentOpt map { file => 
      <attachment url={file.url}> 
      {file.name} 
      </attachment> 
     } getOrElse xml.NodeSeq.Empty 
     } 
    </myObject> 


    println("json = " + json) 
    println("") 
    println("xml = " + myXml) 

} 

chí này sẽ cung cấp cho bạn:

json = {"url":"http://my.dummy.url","filename":"strawberry.png","name":"Erik"} 

xml = <myObject> 
    <author> 
    <name>Erik</name> 
    </author> 
    <attachment url="http://my.dummy.url"> 
     strawberry.png 
     </attachment> 
</myObject> 
Các vấn đề liên quan