Deserialization of json using jackson into specific class using the value of 2 fields to determine the class

Viewed 70

I have to consume an API that uses different JSONs schemas as responses for the same endpoint

This are some examples of the API responses

{
"type": 0,
...
...
}
{
"type": 1,
"amount": 10
...
}

obs: when another_type key is missing amount is a number
{
"type": 1,
"another_type": "FEES",
"amount":
 {
  "original": 20.1,
  "fees": [...]
   ...
   ...
 }
}

obs: when another_type value is FEES amount is an object
{
"type": 1,
"another_type": "OTHER",
"amount":
 {
  "original": 20.1,
  "other": [...]
   ...
   ...
 }
}

obs: when another_type value is OTHER amount is an object (no equal to the previous one)
{
"type": 2,
...
...
}

So my first approach was to do this

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(
    JsonSubTypes.Type(value = Type0::class, name = "0"),
    JsonSubTypes.Type(value = Type1Abstract::class, name = "1"),
    JsonSubTypes.Type(value = Type2::class, name = "2")
)
abstract class Response(
    @field:NotEmpty
    @field:JsonProperty("type")
    open val codeType: Int
)

I added Type1 as another abstract class like this

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "anotherType")
@JsonSubTypes(
    JsonSubTypes.Type(value = Fees::class, name = "FEES"),
    JsonSubTypes.Type(value = NoInfo::class, name = ""),
    JsonSubTypes.Type(value = Other::class, name = "OTHER")
)
abstract class Type1Abstract(
    @field:NotEmpty
    @field:JsonProperty("another_type")
    open val anotherType: String
)

But it did not work because type1 is an abstract class and it can't be initialized (obviously)

Is there any way to use the annotation JsonTypeInfo to resolve this problem?

I need to create one response class or another by checking 2 fields instead of only one

I tried to use @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION, but the documentation says it does not check for the value of the keys which is important in this case

UPDATE: I modified type 1 class and it´s no more an abstract class. Also I added this field

@JsonProperty("amount")
val amount: Any

and this function

private fun ..parse..(another_type: String, amount: Any): BigDecimal {
        return when (amount) {
            is Double -> amount.toBigDecimal()
            is LinkedHashMap<*, *> -> {
                return when (another_type) {
                    "FEES" -> BigDecimal(amount["original"] as String)
                    "OTHER" -> BigDecimal(amount["original"] as String)
                    else -> throw ...
                }
            }
            else -> {
                throw ...
            }
        }
    }

It's not the cleanest way but at it works and I can use it until I found a better solution

0 Answers
Related