2015-07-13 112 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) 
     } 
    }