Skip to content

Unwrapping single value JSON arrays to objects #419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
slomkowski opened this issue Mar 29, 2019 · 4 comments
Closed

Unwrapping single value JSON arrays to objects #419

slomkowski opened this issue Mar 29, 2019 · 4 comments

Comments

@slomkowski
Copy link

It is a common pattern for APIs to return single object wrapped in an array as in following JSON:

[
  {
    "foo": "bar",
    "lorem": "ipsum",
    "num": 42
  }
]

which should be deserialized to:

data class Example(
        val foo: String,
        val lorem: String,
        val num: Int)

Jackson already implements this feature, it can be enabled with property UNWRAP_SINGLE_VALUE_ARRAYS. More info: FasterXML/jackson-databind#381.

It would be nice to have similar option here.

@JakeWharton
Copy link
Contributor

JakeWharton commented Mar 29, 2019

I would advocate against baking any support for this in. You should be able to accomplish this yourself by writing a custom serializer and using @Serializable(with=UnwrapSingleValueArray::class).

@leomillon
Copy link

@slomkowski I faced the same issue and I quickly developed a "workaround" but that's not really the best way to implement it... Anyway, hope it will help to unblock some devs:

import kotlinx.serialization.Decoder
import kotlinx.serialization.Encoder
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.Serializer
import kotlinx.serialization.encode
import kotlinx.serialization.internal.ArrayListSerializer
import kotlinx.serialization.internal.SerialClassDescImpl
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElementSerializer

@Serializer(forClass = List::class)
class UnwrapSingleValueArray<T : Any>(private val dataSerializer: KSerializer<T>) : KSerializer<List<T>> {
    override val descriptor: SerialDescriptor = object : SerialClassDescImpl("UnwrapSingleValueArray") {}

    override fun deserialize(decoder: Decoder): List<T> {
        return when (val jsonElt = JsonElementSerializer.deserialize(decoder)) {
            is JsonArray -> jsonElt.content.map { jsonClient.parse(dataSerializer, it.toString()) }
            else -> listOf(jsonClient.parse(dataSerializer, jsonElt.toString()))
        }
    }

    override fun serialize(encoder: Encoder, obj: List<T>) {
        encoder.encode(ArrayListSerializer(dataSerializer), obj)
    }
}

Note: the jsonClient is just the kotlinx.serialization.json.Json object.

@ghost
Copy link

ghost commented Oct 8, 2019

@slomkowski
To me it's laughable nonsense, there shouldn't be a custom serializer for this simple issue, some people I know just end up replacing first [ and last ] with something more "parsable" from this library.

This is what they are doing:

arrayJsonString.replaceFirst(Regex("\\["), "{\"container\":[").reversed().replaceFirst("]", "}]").reversed()

@Serializable
data class ContainerObj(val container:List<Example>)

data class Example(
        val foo: String,
        val lorem: String,
        val num: Int)

Otherwise rather use gson and be done.

@jnorthrup
Copy link

how cheap is .reverse on megabytes of inner-json?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants