Last active
February 13, 2025 08:24
-
-
Save L-Briand/a8cf83c108b9135de98d9380ec97bb5e to your computer and use it in GitHub Desktop.
A Gson TypeAdapter for Kotlin’s ByteArray to and from Base64 String.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.util.Base64 | |
import com.google.gson.Gson | |
import com.google.gson.JsonParseException | |
import com.google.gson.TypeAdapter | |
import com.google.gson.TypeAdapterFactory | |
import com.google.gson.reflect.TypeToken | |
import com.google.gson.stream.JsonReader | |
import com.google.gson.stream.JsonToken | |
import com.google.gson.stream.JsonWriter | |
/** | |
* A custom Gson `TypeAdapter` implementation for serializing and deserializing `ByteArray` | |
* objects to and from Base64-encoded strings. | |
* | |
* This adapter converts a `ByteArray` to a Base64-encoded string during serialization and | |
* decodes a Base64 string back into a `ByteArray` during deserialization. The adapter also | |
* handles cases like null values and empty strings. | |
* | |
* The companion `Factory` object can be used to register this adapter with Gson for types | |
* of `ByteArray`. | |
* | |
* Serialization: | |
* - Null `ByteArray` values are written as `null`. | |
* - Empty `ByteArray` values are written as an empty string `""`. | |
* - Non-empty `ByteArray` values are encoded into a Base64 string. | |
* | |
* Deserialization: | |
* - Null JSON values are deserialized into `null`. | |
* - Empty strings aŒre deserialized into an empty `ByteArray`. | |
* - Valid Base64 strings are decoded into their corresponding `ByteArray`. | |
* - If an invalid type is encountered, a `JsonParseException` is thrown. | |
*/ | |
class GsonByteArrayB64TypeAdapter : TypeAdapter<ByteArray>() { | |
override fun write(output: JsonWriter?, value: ByteArray?) { | |
output ?: return | |
if (value == null) { | |
// even if output.serializeNull is false, we need to call nullValue() for gson to go onto the next element | |
output.nullValue() | |
return | |
} | |
if (value.isEmpty()) { | |
output.value("") | |
return | |
} | |
val oldHtmlSafeValue = output.isHtmlSafe | |
try { | |
output.isHtmlSafe = false | |
output.value(Base64.encodeToString(value, Base64.NO_WRAP)) | |
} finally { | |
output.isHtmlSafe = oldHtmlSafeValue | |
} | |
} | |
override fun read(input: JsonReader?): ByteArray? { | |
input ?: return null | |
when (input.peek()) { | |
JsonToken.NULL -> { | |
input.nextNull() | |
return null | |
} | |
JsonToken.STRING -> { | |
val stringValue = input.nextString() | |
if (stringValue.isEmpty()) return byteArrayOf() | |
else return Base64.decode(stringValue, Base64.NO_WRAP) | |
} | |
else -> throw JsonParseException("Expected a Base64 String but was ${input.peek()}. At ${input.path}") | |
} | |
} | |
object Factory : TypeAdapterFactory { | |
override fun <T : Any?> create( | |
gson: Gson?, | |
type: TypeToken<T?>? | |
): TypeAdapter<T?>? { | |
if (type == null) return null | |
if (type.rawType != ByteArray::class.java) return null | |
@Suppress("UNCHECKED_CAST") | |
return GsonByteArrayB64TypeAdapter() as TypeAdapter<T?> | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment