Last active
August 29, 2025 13:25
-
-
Save rafaeltoledo/478396e9cdefebc98c147c1de3a33c04 to your computer and use it in GitHub Desktop.
Reddit Serialization
This file contains hidden or 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
| class RedditApi( | |
| private val httpClient: HttpClient, | |
| ) { | |
| suspend fun comments(subreddit: String, id: String): List<RedditResponse> { | |
| val response = httpClient.get { | |
| url { | |
| host = HOST | |
| pathSegments = listOf("r", subreddit, "comments", id) | |
| } | |
| } | |
| return if (response.status.isSuccess()) { | |
| response.body() | |
| } else { | |
| error("Request failed with code: ${response.status}") | |
| } | |
| } | |
| suspend fun frontPage(limit: Int? = null, after: String? = null): RedditListing { | |
| val response = httpClient.get { | |
| url { | |
| host = HOST | |
| limit?.let { parameters.append("limit", it.toString()) } | |
| after?.let { parameters.append("after", it) } | |
| } | |
| } | |
| return if (response.status.isSuccess()) { | |
| response.body<RedditObject>() as RedditListing | |
| } else { | |
| error("Request failed with code: ${response.status}") | |
| } | |
| } | |
| suspend fun subreddit(subreddit: String, after: String?, limit: Int?): RedditResponse { | |
| val response = httpClient.get { | |
| url { | |
| host = HOST | |
| pathSegments = listOf("r", subreddit) | |
| limit?.let { parameters.append("limit", it.toString()) } | |
| after?.let { parameters.append("after", it) } | |
| } | |
| } | |
| return if (response.status.isSuccess()) { | |
| response.body() | |
| } else { | |
| error("Request failed with code: ${response.status}") | |
| } | |
| } | |
| suspend fun top(limit: Int?, after: String?): RedditResponse { | |
| val response = httpClient.get { | |
| url { | |
| host = HOST | |
| pathSegments = listOf("top") | |
| limit?.let { parameters.append("limit", it.toString()) } | |
| after?.let { parameters.append("after", it) } | |
| } | |
| } | |
| return if (response.status.isSuccess()) { | |
| response.body() | |
| } else { | |
| error("Request failed with code: ${response.status}") | |
| } | |
| } | |
| companion object { | |
| private const val HOST = "api.reddit.com" | |
| const val ENDPOINT = "https://$HOST" | |
| } | |
| } |
This file contains hidden or 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 kotlinx.serialization.SerialName | |
| import kotlinx.serialization.Serializable | |
| import kotlin.reflect.KType | |
| import kotlin.reflect.typeOf | |
| @Serializable | |
| data class RedditResponse(val data: RedditListing) | |
| @Serializable(with = RedditSerializer::class) | |
| sealed interface RedditObject | |
| @Serializable | |
| enum class RedditKind(val type: KType) { | |
| @SerialName("t1") | |
| T1(typeOf<RedditComment>()), | |
| @SerialName("t3") | |
| T3(typeOf<RedditLink>()), | |
| @SerialName("Listing") | |
| LISTING(typeOf<RedditListing>()), | |
| @SerialName("more") | |
| MORE(typeOf<RedditMore>()), | |
| } | |
| @Serializable | |
| data class RedditComment( | |
| val body: String, | |
| @SerialName("body_html") val bodyHtml: String, | |
| val controversiality: Int, | |
| val depth: Int, | |
| @SerialName("link_id") val linkId: String, | |
| @SerialName("parent_id") val parentId: String, | |
| /** | |
| * Ugh-asaurus | |
| * | |
| * @return list of comments. Or false. Because yeah. | |
| */ | |
| val replies: RedditObject?, | |
| @SerialName("subreddit_id") val subredditId: String, | |
| // Inherited from RedditSubmission. A little grody | |
| override val author: String, | |
| @SerialName("author_flair_text") override val authorFlairText: String?, | |
| @SerialName("banned_by") override val bannedBy: String?, | |
| override val created: Long, | |
| @SerialName("created_utc") override val createdUtc: Long, | |
| override val gilded: Int, | |
| override val id: String, | |
| override val name: String, | |
| override val saved: Boolean, | |
| override val score: Int, | |
| override val subreddit: String, | |
| override val ups: Int, | |
| ) : RedditObject, RedditSubmission | |
| @Serializable | |
| data class RedditLink( | |
| val clicked: Boolean, | |
| val domain: String?, | |
| val hidden: Boolean, | |
| @SerialName("is_self") val isSelf: Boolean, | |
| @SerialName("link_flair_text") val linkFlairText: String?, | |
| @SerialName("num_comments") val commentsCount: Int, | |
| val permalink: String, | |
| val selftext: String?, | |
| @SerialName("selftext_html") val selftextHtml: String?, | |
| val stickied: Boolean, | |
| val thumbnail: String, | |
| val title: String, | |
| val url: String, | |
| val visited: Boolean, | |
| @SerialName("post_hint") val postHint: String?, | |
| // Inherited from RedditSubmission. A little grody | |
| override val author: String, | |
| @SerialName("author_flair_text") override val authorFlairText: String?, | |
| @SerialName("banned_by") override val bannedBy: String?, | |
| override val created: Long, | |
| @SerialName("created_utc") override val createdUtc: Long, | |
| override val gilded: Int, | |
| override val id: String, | |
| override val name: String, | |
| override val saved: Boolean, | |
| override val score: Int, | |
| override val subreddit: String, | |
| override val ups: Int, | |
| ) : RedditObject, RedditSubmission | |
| @Serializable | |
| data class RedditListing( | |
| val after: String? = null, | |
| val before: String? = null, | |
| val children: List<RedditObject>, | |
| val modhash: String, | |
| ) : RedditObject | |
| @Serializable | |
| data class RedditMore( | |
| val count: Int, | |
| val name: String, | |
| val id: String, | |
| @SerialName("parent_id") val parentId: String, | |
| val depth: Int, | |
| val children: List<String>, | |
| ) : RedditObject | |
| interface RedditSubmission { | |
| val author: String | |
| @SerialName("author_flair_text") | |
| val authorFlairText: String? | |
| @SerialName("banned_by") | |
| val bannedBy: String? | |
| val created: Long | |
| @SerialName("created_utc") | |
| val createdUtc: Long | |
| val gilded: Int | |
| val id: String | |
| val name: String | |
| val saved: Boolean | |
| val score: Int | |
| val subreddit: String | |
| val ups: Int | |
| } |
This file contains hidden or 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 kotlinx.serialization.KSerializer | |
| import kotlinx.serialization.descriptors.SerialDescriptor | |
| import kotlinx.serialization.descriptors.buildClassSerialDescriptor | |
| import kotlinx.serialization.descriptors.element | |
| import kotlinx.serialization.encoding.CompositeDecoder | |
| import kotlinx.serialization.encoding.Decoder | |
| import kotlinx.serialization.encoding.Encoder | |
| import kotlinx.serialization.encoding.decodeStructure | |
| import kotlinx.serialization.json.JsonDecoder | |
| import kotlinx.serialization.json.JsonElement | |
| import kotlinx.serialization.serializer | |
| object RedditSerializer : KSerializer<RedditObject> { | |
| override val descriptor: SerialDescriptor = buildClassSerialDescriptor("RedditObject") { | |
| element<RedditKind>("kind") | |
| element<JsonElement>("data") | |
| } | |
| override fun deserialize(decoder: Decoder): RedditObject { | |
| if (decoder !is JsonDecoder) { | |
| error("Unsupported decoder format: ${decoder::class.simpleName}") | |
| } | |
| return decoder.decodeStructure(descriptor) { | |
| var kind: RedditKind? = null | |
| var data: JsonElement? = null | |
| while (true) { | |
| when (val index = decodeElementIndex(descriptor)) { | |
| 0 -> kind = decodeSerializableElement(descriptor, 0, RedditKind.serializer()) | |
| 1 -> data = decodeSerializableElement(descriptor, 1, JsonElement.serializer()) | |
| CompositeDecoder.DECODE_DONE -> break | |
| else -> error("Unexpected index: $index") | |
| } | |
| } | |
| require(kind != null && data != null) | |
| decoder.json.decodeFromJsonElement(serializer(kind.type), data) as RedditObject | |
| } | |
| } | |
| override fun serialize(encoder: Encoder, value: RedditObject) { | |
| if (encoder !is JsonDecoder) { | |
| error("Unsupported encoder format: ${encoder::class.simpleName}") | |
| } | |
| TODO("Not implemented yet!") | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment