【问题标题】:json4s parse json partiallyjson4s 部分解析 json
【发布时间】:2015-07-13 10:37:30
【问题描述】:

我有一个 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: ???)

【问题讨论】:

    标签: json scala json4s


    【解决方案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": "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)
    

    【讨论】:

    • 嘿,谢谢,您的回复让我走上了正轨。我只需要一些临时解决方案,因此我根据您的答案发布了自己的答案,以说明这可以用更少的代码实现。对于其他阅读本文的人来说,这个答案是正确的方法,但如果你需要快速的东西,我的答案也可以。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-07
        • 2018-12-06
        相关资源
        最近更新 更多