json4 秒部分解析 json
json4s parse json partially
我有一个 json 模型,其中某些属性的内容取决于另一个属性。像这样:
"paymentMethod": "CREDIT_CARD",
"metaData": {
"cardType": "VISA",
"panPrefix": "",
"panSuffix": "",
"cardHolder": "",
"expiryDate": ""
}
因此,当 paymentMethod
等于 CREDIT_CARD
时,metadata
对象将包含所描述的属性。如果是其他支付方式,会有不同的元数据。
我想以面向未来的方式处理这种情况。我想要做的是不立即解析 metadata
字段,而是以某种方式保留它 "unparsed" 直到我解析了 paymentMethod
字段。然后我会获取元数据并应用适当的解析方法。
但是我不知道对于此类 "late parsed" 属性的 Scala class 字段使用哪种类型。 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: ???)
您可以使用 Map[String, String]
。
它将包含您可能需要的任何内容。
您可以创建一个 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)
Peter Neyens 的回答启发了我实施自己的解决方案。它不像他的那样通用,但就我而言,我需要一些非常简单和临时的东西。这是我所做的:
可以定义一个案例class,未知类型的字段由JObject
类型表示。像这样:
case class PaymentGatewayResponse(
default: Boolean,
paymentMethod: String,
visibleForCustomer: Boolean,
active: Boolean,
metaData: JObject)
当这样的json被解析成这样的caseclass时,这个字段并没有立即被解析,而是包含了所有必要的信息。然后可以在单独的步骤中解析它:
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)
}
}
我有一个 json 模型,其中某些属性的内容取决于另一个属性。像这样:
"paymentMethod": "CREDIT_CARD",
"metaData": {
"cardType": "VISA",
"panPrefix": "",
"panSuffix": "",
"cardHolder": "",
"expiryDate": ""
}
因此,当 paymentMethod
等于 CREDIT_CARD
时,metadata
对象将包含所描述的属性。如果是其他支付方式,会有不同的元数据。
我想以面向未来的方式处理这种情况。我想要做的是不立即解析 metadata
字段,而是以某种方式保留它 "unparsed" 直到我解析了 paymentMethod
字段。然后我会获取元数据并应用适当的解析方法。
但是我不知道对于此类 "late parsed" 属性的 Scala class 字段使用哪种类型。 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: ???)
您可以使用 Map[String, String]
。
它将包含您可能需要的任何内容。
您可以创建一个 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)
Peter Neyens 的回答启发了我实施自己的解决方案。它不像他的那样通用,但就我而言,我需要一些非常简单和临时的东西。这是我所做的:
可以定义一个案例class,未知类型的字段由JObject
类型表示。像这样:
case class PaymentGatewayResponse(
default: Boolean,
paymentMethod: String,
visibleForCustomer: Boolean,
active: Boolean,
metaData: JObject)
当这样的json被解析成这样的caseclass时,这个字段并没有立即被解析,而是包含了所有必要的信息。然后可以在单独的步骤中解析它:
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)
}
}