我有一个json模型,其中某些属性的内容依赖于其他属性。像这样:
"paymentMethod": "CREDIT_CARD",
"metaData": {
"cardType": "VISA",
"panPrefix": "",
"panSuffix": "",
"cardHolder": "",
"expiryDate": ""
}
因此,当paymentMethod
等于CREDIT_CARD
时,metadata
对象将包含所述的属性。在其他付款方式的情况下,会有不同的元数据。
我希望以面向未来的方式处理这种情况。我要做的是不立即解析metadata
字段,但在我解析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: ???)
答案 0 :(得分:4)
您可以创建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": "foo@bar.com"
},
"otherField": "world"
}
""")
val cc = json.extract[PaymentResponse]
// PaymentResponse(CreditCardPayment(VISA,2015),hello)
val pp = json2.extract[PaymentResponse]
// PaymentResponse(PayPalPayment(foo@bar.com),world)
答案 1 :(得分:0)
您可以使用Map[String, String]
。
它将包含您可能需要的任何内容。
答案 2 :(得分:0)
Peter Neyens的回答激励我实施自己的解决方案。它并不像他那样通用,但就我而言,我需要一些非常简单和特别的东西。这就是我所做的:
可以定义一个案例类,其中未知类型的字段由JObject
类型表示。像这样:
case class PaymentGatewayResponse(
default: Boolean,
paymentMethod: String,
visibleForCustomer: Boolean,
active: Boolean,
metaData: JObject)
当将这样的json解析为这样的case类时,不会立即解析该字段,而是包含所有必要的信息。然后可以在一个单独的步骤中解析它:
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)
}
}