What is a simple MapRowParser in Anko?
Asked Answered
T

1

7

I have read the docs for Anko SQLite.

I know that creating a simple RowParser iscan be done by doing val rowParser = classParser<Person>()

The classParser is a function defined in the Anko-SQLite source code.

How can I get a simple MapRowParser?

Terpsichorean answered 5/11, 2017 at 6:57 Comment(2)
See https://mcmap.net/q/1623848/-why-does-kotlin-for-android-developers-the-book-need-to-add-extensions-parselist-againEuxenite
Thanks, do you mean that Anko havn't predefined MapRowParser just like classParser? I have to write a MapRowParser by myself ?Terpsichorean
B
6

If you go on Github and do this search, you'll see that there are two files where MapRowParser is referenced in any way.

The first file contains this:

interface MapRowParser<out T> {
    fun parseRow(columns: Map<String, Any?>): T
}

Which shows MapRowParser as an interface.

However. According to the search there are two files where MapRowParser is mentioned. If you look through the files, you'll see that there are no classes. According to this question which shows a manual implementation of the class, it has to be manually implemented. In addition, the code for Anko doesn't show any classes that implements MapRowParser.

So, you have to create a class that implements MapRowParser on your own. If I have read the docs and code right, the map itself is automatically passed but what the parser does is handle the data you receive.


Equivalently with the RowParser. it's an interface. However, there is a method that returns a specific parser. There is nothing like that with RowMapParser though.

EDIT:

Going into the source code shows that the two types of single row parsers are used for a few different types. I think the reason there is no MapRowParser is because it's too hard writing a good, general map parser. Maps have different behaviors in general as they have a key and a value, while a List only has a value that you cast as a type and return:

private class SingleColumnParser<out T> : RowParser<T> {
    override fun parseRow(columns: Array<Any?>): T {
        if (columns.size != 1)
            throw SQLiteException("Invalid row: row for SingleColumnParser must contain exactly one column")
        @Suppress("UNCHECKED_CAST")
        return columns[0] as T//Right here it just casts the column as the type defined when creating
    }
}

You could do the same with Maps, but the key would be lost. In addition, checking through the source code you see that the data passed into the parser only contains one column.

Digging into the source a bit further also reveals this method:

private fun readColumnsMap(cursor: Cursor): Map<String, Any?> {
    val count = cursor.columnCount
    val map = hashMapOf<String, Any?>()
    for (i in 0..(count - 1)) {
        map.put(cursor.getColumnName(i), cursor.getColumnValue(i))
    }
    return map
}

If I have read the source right, the above method converts the entire row into a single Map, and takes the name of the column with it. So you end up with something like this for a single row:

Col1 -> Row1col1val
Col2 -> Row1col2val
...

The system runs on cursors, which can be seen in the methods to parse multiple entries in a List or Map:

moveToFirst()
while (!isAfterLast) {
    list.add(parser.parseRow(readColumnsMap(this)))//adds the result into a pre-defined list to return
    moveToNext()
}

Which again shows that writing a generic is hard, because there has to be a return value that makes sense, which is hard to do if you don't know what kind of data to put into a single return value.

And that is something that's too hard* to write a generic parser for, because you can never be sure about the amount of rows, what is to be done with the values, etc. So in order to write your own parser, you create a class that implements MapRowParser and use this to parse the data you need. FOr an instance by assigning ID's to a class stored as a blob, putting the data into a data class, whatever you use it for.

*It's too hard to write because you can never be sure how one developer will need the data. When you have it as a map you can't just return a single value, because all the other data would be lost. So you'd have to return it as a map if it's needed for a general parser, and then the developer would still end up having to parse the data. With the Lists it's easy just returning a single value. But with Maps, in order to not lose any data, the parser essentially becomes useless if it's written for a standardized purpose.

Basophil answered 11/11, 2017 at 11:0 Comment(3)
Thanks! Why does Anko write classParser? but why doesn't Anko write a implements of MapRowParser ? I feel it's very strange!Terpsichorean
I don't know why they didn't implement it. Could be because it's a too complicated type to implement for general usage. Though if you want an accurate answer to that, you have to ask the creators.Basophil
@Terpsichorean did some more code digging and edited the answer.Basophil

© 2022 - 2024 — McMap. All rights reserved.