2015-07-13 109 views
0

我有一個json模型,其中某些屬性的內容取決於其他屬性。事情是這樣的:json4s部分解析json

"paymentMethod": "CREDIT_CARD", 
"metaData": { 
    "cardType": "VISA", 
    "panPrefix": "", 
    "panSuffix": "", 
    "cardHolder": "", 
    "expiryDate": "" 
} 

所以當paymentMethod等於CREDIT_CARD,在metadata對象將作爲描述包含屬性。如果使用其他付款方式,則會有不同的元數據。

我想以面向未來的方式處理這種情況。我試圖做的是不立即解析metadata字段,但保持它以某種方式「unparsed」,直到我解析paymentMethod字段。然後我會採取元數據並應用適當的解析方法。

但是,我不知道哪種類型用於Scala類字段中的這種「遲分析」屬性。我試過String,JsonInput,JObject,它們都不適合(要麼不編譯,要麼不能解析)。我可以使用哪種類型的想法?或換句話說:

case class CreditCardMetadata(
    cardType: String, 
    panPrefix: String, 
    panSuffix: String, 
    cardHolder: String, 
    expiryDate: String) 

case class PaypalMetadata(...) // etc. 

case class PaymentGatewayResponse(
    paymentMethod: String, 
    metadata: ???) 

回答

1

您可以創建一個CustomSerializer來直接解析元數據。喜歡的東西:

case class PaymentResponse(payment: Payment, otherField: String) 

sealed trait Payment 
case class CreditCardPayment(cardType: String, expiryDate: String) extends Payment 
case class PayPalPayment(email: String) extends Payment 

object PaymentResponseSerializer extends CustomSerializer[PaymentResponse](format => ( 
    { 
    case JObject(List(
      JField("paymentMethod", JString(method)), 
      JField("metaData", metadata), 
      JField("otherField", JString(otherField)) 
     )) => 
     implicit val formats = DefaultFormats 
     val payment = method match { 
     case "CREDIT_CARD" => metadata.extract[CreditCardPayment] 
     case "PAYPAL" => metadata.extract[PayPalPayment] 
     } 
     PaymentResponse(payment, otherField) 
    }, 
    { case _ => throw new UnsupportedOperationException } // no serialization to json 
)) 

哪些可以作爲:

implicit val formats = DefaultFormats + PaymentResponseSerializer 

val json = parse(""" 
     { 
     "paymentMethod": "CREDIT_CARD", 
     "metaData": { 
      "cardType": "VISA", 
      "expiryDate": "2015" 
     }, 
     "otherField": "hello" 
     } 
     """) 

val json2 = parse(""" 
    { 
     "paymentMethod": "PAYPAL", 
     "metaData": { 
      "email": "[email protected]" 
     }, 
     "otherField": "world"   
    } 
    """) 

val cc = json.extract[PaymentResponse] 
// PaymentResponse(CreditCardPayment(VISA,2015),hello) 
val pp = json2.extract[PaymentResponse] 
// PaymentResponse(PayPalPayment([email protected]),world) 
+0

嘿,謝謝,你的迴應使我走上了正軌。我只需要一些臨時的解決方案,所以我貼我自己的答案基於你的,就說明了這可以用更少的代碼來實現。 對於其他人讀這篇文章,這個答案是正確的道路要走,但如果你需要的東西很快,我的答案會工作,太。 – Haspemulator

0

您可以使用Map[String, String]。 它將包含您可能需要的任何東西。

0

彼得Neyens答案啓發我實現我自己的解決方案。它不像他那般通用,但在我的情況下,我需要一些非常簡單和特別的東西。下面是我做了什麼:

這是可能的定義與未知類型的字段的情況下,類由JObject類型表示。事情是這樣的:

case class PaymentGatewayResponse(
    default: Boolean, 
    paymentMethod: String, 
    visibleForCustomer: Boolean, 
    active: Boolean, 
    metaData: JObject) 

當這樣的JSON解析成這樣的情況下階層,這個領域不立即解析,但包含了所有必要的信息。然後有可能在一個單獨的步驟解析它:

case class CreditCardMetadata(
    cardType: String, 
    cardObfuscatedNumber: String,  
    cardHolder: String, 
    expiryDate: String) 

val response: PaymentGatewayResponse = doRequest(...) 
response.map { r => 
     r.paymentMethod match { 
     case "CREDIT_CARD" => r.metaData.extract[CreditCardMetadata] 
     case unsupportedType: String => throw new UnsupportedPaymentMethodException(unsupportedType) 
     } 
    }