Generate a fromString() method for data classes


#1

It would be really nice if there were a way to recreate a data class from the string it outputs.

My use case is an Android app that uses profiles, represented by a Profile data class. They’re persisted by storing the Profile.toString() in the Android shared preferences. Then when the app loads again, I need to reconstruct a Profile from the string. Right now I’m using json and overriding toString(), which works but requires me to hard-code the keys for each of the fields – not a very general solution.

If there’s a way to write this as an extension function, that would be awesome, but I don’t think there’s a way to get the name of a val/var without language support.


#2

There are some tricky questions about how to parse.

Simplest case:

data class Place(val name: String)

val ambiguousString = "Place(name=park, title=field)"
val howShouldWeParseThis = Place.fromString(amgbiguousString)

val option1 = Place("park, title=field") // Longest possible
val option2 = Place("park")              // Shortest possible

More complex

data class Person(val name: String, nickname: String)

val ambiguousString = "Person(name=I, nickname=we nickname=us)"
val howShouldWeParseThis = Person.fromString(ambiguousString)

val option1 = Person("I", "we, nickname=us") // First, longest possible declaration
val option2 = Person("I, nickname=we", "us") // Last, longest possible declaration
val option3 = Person("I", "we")              // First, shortest possible declaration
val option4 = Person("I", "us")              // Last, shortest possible declaration

Hard

data class Vacation(val person: Person, val place: Place)

// These both have obvious correct answers that are hard (impossible?) to parse
// using the same set of rules.
val closedParens = "Vacation(person=Person(name=me, nickname=place=Place(name=here)), place=Place(name=here))"
val openParen = "Vacation(person=Person(name=me, nickname=place=Place(name=here), place=Place(name=here))"

val correctClosed = Vacation(Person("me", "place=Place(name=here)"), Place("there"))
val correctOpen = Vacation(Person("me", "place=Place(name=here"), Place("there"))

#3

Probably the most elegant solution would be, if you can’t perfectly parse the string, throw an exception, and the string contained in the exception is the best-effort parsing (probably, the first, shortest values, with invalid parts ignored). So, the caller can catch it (or not) and then decide for themselves whether to use it or throw it away.


#4

Sounds like you want something that reads and writes JSON from Kotlin data classes. You might want to have a look at Jackson (https://github.com/FasterXML/jackson-module-kotlin). Klaxon (https://github.com/cbeust/klaxon) also seems to have good support for “rendering” Kotlin classes to JSON, but nothing automatic for the reverse.


#5

Jackson does look like what I want, thank you.


#6

If you don’t need the type to be mutable you might want to try auto value with the parcellable extension

http://ryanharter.com/blog/2016/03/22/autovalue/


#7

I don’t need anything to be mutable. It looks like gson might also do what I want.