2012-06-25 196 views
6

我試圖將來自REST API的響應建模爲案例類,我可以使用模式匹配。斯卡拉案例類建模

我認爲這將是一個很好的假設繼承,但我發現這已被棄用。 我知道已經有關於案例類和繼承的問題,但是我的問題更多地是關於如何在這裏繼承而不是繼承。

我開始用下面的兩個案例類,做工精細:

case class Body(contentType: String, content: String) 
case class Response(statusCode: Int, body: Body) 

即一個REST調用將喜歡的東西返回:

Response(200, Body("application/json", """{ "foo": "bar" }""")) 

,我可以的模式匹配,如:

response match { 
    case Response(200, Body("application/json", json)) => println(json) 
    case Response(200, Body("text/xml", xml)) => println(xml) 
    case Response(_,_) => println("Something unexpected") 
} 

等工作正常。

我在哪裏遇到了麻煩是:我想這些情況下類的輔助擴展,如:

case class OK(body: Body) extends Response(200, body) 
case class NotFound() extends Response(404, Body("text/plain", "Not Found")) 

case class JSON(json: String) extends Body("application/json", json) 
case class XML(xml: String) extends Body("text/xml", xml) 

,這樣我可以做簡單的圖案像這樣的比賽:

response match { 
    case OK(JSON(json)) => println(json) 
    case OK(XML(xml)) => println(xml) 
    case NotFound() => println("Something is not there") 

    // And still drop down to this if necessary: 
    case Response(302, _) => println("It moved") 
} 

也可以讓我的REST代碼直接使用並返回:

Response(code, Body(contentType, content)) 

這是e asier動態地構建響應。

case class OK(override val body: Body) extends Response(200, body) 

然而,這似乎並沒有與模式匹配的工作:

通過

所以......

我可以得到它來編譯(用廢棄警告)。

Response(200, Body("application/json", "")) match { 
    case OK(_) => ":-)" 
    case _ => ":-(" 
} 
res0: java.lang.String = :-(

有關如何工作的任何想法?我接受不同的方法,但這是我嘗試尋找案例類別的實際用途

回答

10

爲什麼案例類別shouldn't be subclassed有幾個原因。在你的情況下,問題變成OK是另一種類型(Response的子類型),因此匹配失敗(即使參數匹配,類型不匹配)。

改爲您需要custom extractors。例如:

case class Response(code: Int, body: String) 
object OK { 
    def apply(body: String) = Response(200, body) 
    def unapply(m: Response): Option[String] = m match { 
    case Response(200, body) => Some(body) 
    case _     => None 
    } 
} 

def test(m: Response): String = m match { 
    case OK(_) => ":-)" 
    case _  => ":-(" 
} 

test(Response(300, "Hallo")) // :-(
test(Response(200, "Welt")) // :-) 
test(OK("Welt"))    // :-) 

this thread中有幾個自定義提取器的例子。

+0

啊,謝謝 - 我看到我完全錯過了直到此爲止的無用的目的;這非常有幫助。將我的代碼完全測試出來,以確保我已經覆蓋並且將在今晚晚些時候接受。 – 7zark7

+0

好答案@Sciss。自定義提取器是我對Scala很喜歡的一件事。 – mergeconflict

+0

@ 7zark7請注意,當您使用自定義提取器時,您將失去密封類的詳盡性保證。 –

1

你看過未經過濾的scala庫嗎? http://unfiltered.lessis.me/ 它可能會幫助你接近你的問題。 HTH

+0

我看了一看,但我放棄了,因爲幻燈片太多,每句一個/幾個字。是否有任何單頁版本可以闡明Unfiltered的全部內容? – KajMagnus

+0

可能是這一個更好的幫助:https://github.com/softprops/Unfiltered – AndreasScheinert

1

儘管0__提到的自定義提取器當然可以使用,但您將失去密封類型層次結構的全面性保證。雖然在問題中給出的例子中沒有任何sealed,但問題很適合他們。

在這種情況下,我的建議是簡單地確保case class始終位於類型層次結構的底部,並使上層類正常。例如:

sealed class Response(val statusCode: Int, val body: Body) sealed 
case class Ok(override val body: Body) extends Response(200, body) 
sealed class NotOk(statusCode: Int, body: Body) extends Response(statusCode, body) 
case object NotFound extends NotOk(404, "Not found") 
// and so on... 
+0

感謝丹尼爾,雖然我的第一印象是,如果我還想讓響應匹配響應,這是行不通的 - 我看到這可能工作,如果我定義在Sciss提到的Response對象上不應用,並且將「助手」作爲案例類。今天會嘗試兩種方法,看看最適合/最適合這裏。 – 7zark7

+0

你的意思是寫'密封類響應'嗎? –

+0

@Sciss是,還有'NotOk'。感謝您指出我的錯誤。 –