Einleitung: Warum Ihr Kotlin-Code plötzlich Jackson "zerstört"?
\n\nHaben Sie jemals in Kotlin eine schöne, prägnante data class geschrieben, ein paar Felder hinzugefügt, die JSON-Deserialisierung über Jackson gestartet – und plötzlich eine lange, erschreckende Fehlermeldung erhalten? Kommt Ihnen bekannt vor? Wenn ja, sind Sie nicht allein. Der Fehler "No Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator" ist eine der häufigsten Anfragen auf Stack Overflow (tausende Aufrufe!) unter Entwicklern, die von Java auf Kotlin umsteigen.
In diesem Artikel übersetzen wir nicht einfach den Fehlertext. Wir untersuchen ihn "unter dem Mikroskop": warum er auftritt, wie man ihn reproduziert, und vor allem – wie man ihn ein für alle Mal loswird. Sie erhalten nicht nur Theorie, sondern auch fertige, getestete Lösungen mit Kotlin-Code. Los geht's!
\n\nWas bedeutet dieser Fehler überhaupt? Einfach erklärt
\n\nStellen wir uns vor, Jackson sei ein "Baumeister", der aus JSON-Bausteinen ein Objekt Ihrer Klasse zusammensetzen muss. Um ein Haus zu bauen, braucht der Baumeister einen verständlichen Plan (Konstruktor) oder zumindest einen Satz fertiger Wände (Felder).
\n\nDer Fehler "No Creators, like default construct, exist" sagt wörtlich: "Sehr geehrter Jackson, ich kann kein Objekt dieser Klasse erstellen, weil sie keinen Standardkonstruktor (ohne Parameter) hat, und ich weiß nicht, wie ich es aus den übergebenen JSON-Feldern zusammensetzen soll!"
\n\nIn Java ist alles einfach: Wenn Sie keinen Konstruktor schreiben, erstellt der Compiler freundlicherweise einen leeren Standardkonstruktor. Jackson sieht ihn und verwendet ihn freudig. In Kotlin hingegen wird, wenn Sie eine data class mit Parametern im Konstruktor definieren, der parameterlose Konstruktor nicht automatisch erstellt. Jackson sucht aber standardmäßig genau danach.
Das Problem reproduzieren: Code, der mit einem Fehler abstürzt
\n\nSchreiben wir ein einfaches Beispiel, das diesen Fehler garantiert auslöst. Stellen wir uns vor, wir haben eine Benutzerklasse:
\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport com.fasterxml.jackson.module.kotlin.readValue\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\n\n// ACHTUNG: Dieser Code löst einen Fehler aus!\ndata class User(val name: String, val age: Int)\n\nfun main() {\n val mapper: ObjectMapper = jacksonObjectMapper()\n val json = """{"name": "Alexej", "age": 30}"""\n\n // Versuchen, JSON in ein User-Objekt zu deserialisieren\n val user: User = mapper.readValue(json)\n println(user)\n}\n\n\nFühren Sie diesen Code aus, und Sie erhalten die berühmte Ausnahme:
\n\ncom.fasterxml.jackson.databind.exc.InvalidDefinitionException: \nCannot construct instance of `User` (no Creators, like default construct, exist): \ncannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (String)"{"name": "Alexej", "age": 30}"; line: 1, column: 2]\n\n\nDa ist sie, unser "Star" von Stack Overflow! Warum ist das passiert? Weil data class User(val name: String, val age: Int) keinen parameterlosen Konstruktor hat. Alle seine Felder sind im primären Konstruktor deklariert und obligatorisch (val). Jackson kann User nicht erstellen, ohne zu wissen, welche Werte an name und age übergeben werden sollen, und ohne Hinweise in Form von Settern.
3 Möglichkeiten, den Fehler zu beheben (vom Einfachen zum Richtigen)
\n\nDie gute Nachricht: Es gibt mehrere Lösungen. Die Wahl hängt davon ab, wie sehr Sie den Prozess kontrollieren möchten und welchen Code Sie als "sauber" betrachten.
\n\nMethode 1: Die einfachste (aber nicht für Kotlin-Puristen)
\n\nManuell einen parameterlosen Konstruktor hinzufügen. In Kotlin geschieht dies durch Angabe von Standardwerten für alle Felder.
\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport com.fasterxml.jackson.module.kotlin.readValue\nimport com.fasterxml.jackson.module.kotlin.jacksonObjectMapper\n\n// Ja