Is there a way I can use lenses to decide which ob...
# http4k
a
Is there a way I can use lenses to decide which object types to create from a response? Say I have a list
attachments
of type
Attachment
but it can be either a
FileAttachment
og
PullRequest
or even something else. I can make lenses for both subtypes and the supertype but I don’t understand how to check if it is one or the other? To be clear, the two types are mixed in the response.
I mean..I can just take the
id
and
isUpload
field and discriminate between them, and then make a separate response to get them one by one, but that sounds extremely tedious and bad 😞
d
Not with lenses, but you can configure this at the level of the Json library. Assuming Jackson - you can make it do polymorphic deserialisation based on a discriminator field.
a
Do you have an example in the docs somewhere? Or is this strictly jackson specific (or rather json library specific) ?
s
@Arnab do you have an example of your use case? By mentioning upload I suspect you may be referring to multipart bodies, in which case you can refer to these docs for examples of how to deal with them (including type safe approach)
a
So, my usecase is as follows: We are looking into migrating our trello board into github issues. I actually think http4k is a really good tool for this as it allows me to take a bunch of responses from the trello api and check that type conversion works properly before migrating anything into github. Trello issues might have multiple types of attachments: ◦ files of type image/jpeg, image/png, .mov ◦ github PR (mostly a link to a github PR) ◦ ..other types of attachments that I haven’t thought about. For now, I ended up doing this:
Copy code
callTrelloApi("cards/${id}/attachments").let {
        it.bodyString().asJsonObject().map { attachment ->
            attachment["isUpload"].booleanValue() to
                    Response(Status.OK).with(Body.json().toLens() of attachment)
        }.map { (isUpload, attachmentResponse) ->
            if (isUpload) fileAttachmentLens(attachmentResponse) else githubPRLens(attachmentResponse)
        }
    }
This sorta works, but I do wish there was a way to write :
Copy code
val attachmentLens = Body.auto<Attachment>().toLens()
and then link it somehow to the lenses I created for
FileAttachment
and
GithubPRAttachment
but doesn’t seem to be the case 😛
a
Jackson polymorphic deserialization is pretty simple if you have a dedicated "type" field you can check. I don't have any OSS examples, but this SO answer shows a Java example. Then you can just use a lens for the abstract class. If you're using http4k-contract, you may need to use
JsonTypeInfo.As.EXISTING_PROPERTY
and add the property to the base class rather than it be implied. https://stackoverflow.com/a/30386694/1253613
Oh what the heck. This DTO example is in a public API anyway.
Copy code
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes(
    JsonSubTypes.Type(value = IntOptionsDtoV5.Range::class, name = "range"),
    JsonSubTypes.Type(value = IntOptionsDtoV5.List::class, name = "list"),
)
sealed class IntOptionsDtoV5(val type: String) {
    data class Range(val min: Int, val max: Int): IntOptionsDtoV5("range")
    data class List(val values: kotlin.collections.List<Int>): IntOptionsDtoV5("list")

    companion object {
        val lens = Jackson.autoBody<IntOptionsDtoV5>().toLens()
    }
}
a
Interesting, maybe it can discriminate based on a Boolean too. Thanks! I appreciate it!