json4s部分解析json

时间:2015-07-13 10:37:31

标签: json scala json4s

我有一个json模型,其中某些属性的内容依赖于其他属性。像这样:

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

因此,当paymentMethod等于CREDIT_CARD时,metadata对象将包含所述的属性。在其他付款方式的情况下,会有不同的元数据。

我希望以面向未来的方式处理这种情况。我要做的是不立即解析metadata字段,但在我解析paymentMethod字段之前保持它“解开”。然后我将采用元数据并应用适当的解析方法。

但是我不知道Scala类字段使用哪种类型来处理这种“后期解析”属性。我已经尝试了StringJsonInputJObject,但它们都不合适(要么不编译,要么无法解析)。我可以使用哪种类型的想法?或者,换句话说:

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

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

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

3 个答案:

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