Spring Data REST. Subclass handling - spring-data-rest

I try to POST the object
{
"id": "SOME_ID",
"foo": "http://localhost:8080/api/subclass-of-foo/OBJECT_ID"
}
The Foo Class is abstract. Information about concrete class is in the link subclass-of-foo. But I get always
"Cannot construct instance of Foo (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information".
I can't use #JsonDeserialize(as = Subclass.class) on Foo, because there are multiple sub-classes of Foo.
Where and how can I provide this information to fix the problem?
#Entity
#Inheritance(strategy = SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = STRING)
class abstract Foo {
private id;
}
class Bar1 extends Foo {
}
class Bar2 extends Foo {
}
class Bar3 extends Foo {
}
#Entity
class X {
private String id;
#OneToOne
private Foo foo;
}
And json payload to POST an X object:
{
"id": "X_ID",
"foo": "http://localhost:8080/api/bar1/Bar1_ID"
}

Related

Tell Kotlin that a generic type is an interface

Let's say I have a class Foo
class Foo {
val noProblem = "Hakuna Matata"
}
I now want to decorate it with an ID. Kotlin's delegates make this relatively painless:
class IdentifiableFoo(
val id: Int,
foo: Foo,
): Foo by foo
interface Foo {
val noProblem: String
}
class FooImpl: Foo {
override val noProblem = "Hakuna Matata"
}
but let's say I have another class Bar that I also want to decorate, and then another Baz, and then another ...
I could just create a IdentifiableXYZ for each of them, of course.
But what I really want is something akin to
class Identifiable<T> (
val id: Int,
thing: T
): T by thing
That I could just use for all of them.
And yes, there's a very good chance that the language doesn't support something like that, but the error message made me think:
Only classes and interfaces may serve as supertypes
so can I do some where magic or something to tell Kotlin that T is required to be an interface?
It is not possible in Kotlin. Kotlin only allows super type to be either a class or an interface. Type parameter cannot be a super type of some class or interface.
One workaround might be using a base interface for all the interfaces you are using. But not sure it solves your problem.
class IdentifiableFoo<T : BaseFoo>(
val id: Int,
val foo: T,
) : BaseFoo by foo {
fun doSomething(a: T.() -> String) {
println(a.invoke(foo))
}
}
interface Foo0 : BaseFoo {
val noProblem: String
}
interface Foo1 : BaseFoo {
val someProblem: String
}
class FooImpl : Foo0 {
override val noProblem = "Hakuna Matata"
}
class Foo1Impl() : Foo1 {
override val someProblem: String = "Some Problem"
}
interface BaseFoo {}
Usage:
IdentifiableFoo<Foo0>(2, FooImpl()).doSomething {
this.noProblem
} // Prints "Hakuna Matata"
IdentifiableFoo<Foo1>(2, Foo1Impl()).doSomething {
this.someProblem
} // Prints "Some Problem"
Playground link

Cannot call a function from a child type from a constructor

Is it possible to call a function from a child type from a constructor? Please take a look at the example
class Dog(animalType: DogType) : Animal(animalType) {
fun doSomething() {
animalType.runDogTypeFunction() // error but animalType is always DogType
}
}
abstract class Animal(val animalType: AnimalType)
interface AnimalType
enum class DogType() : AnimalType {
DOG1, DOG2;
fun runDogTypeFunction() {}
}
enum class CatType() : AnimalType {
CAT1, CAT2;
fun runCatTypeFunction() {}
}
animalType is const (val) so it always is DogType. I do not understand why I cannot call a method from the DogType class.
I tried to override val but I received NPE
Your property is declared in the base Animal class (as AnimalType). The constructor param in Dog doesn't exist by the time you call doSomething.
You could try something like this:
abstract class Animal<T: AnimalType>(val animalType: T)
class Dog(animalType: DogType) : Animal<DogType>(animalType) {
fun doSomething() {
animalType.runDogTypeFunction()
}
}

Property getter typed on Supertype instead of Implementation in Kotlin

Suppose I have two classes, a Baseand a Implwhich extends Base.
package mypackage
open class Base
class Impl : Base()
How would I create a private property for the concrete Impl-Type (for internal use), with a public getter typed as the Base-Type, to achieve polymorphism? My initial approach was like this:
class Test {
private val myType = Impl()
get():Base
}
However, the Kotlin compiler complains:
Error:(30, 11) Kotlin: Getter return type must be equal to the type of the property, i.e. 'mypackage.Impl'
Basically, this is what it would look like in plain Java:
public class Test {
private Impl myImpl = new Impl();
public Base getBase() {
return myImpl;
}
}
How could one achieve this? Am I missing something?
P.S. I am aware of Backing Fields and creating custom methods as a workaround for getter, I was just curious on how to approach this in an elegant, Kotlin style manner.
If the property is private, so will be the getter. In this case, it doesn't matter what type it will have. If you want to have a public property of base type, you'll need to declare it separately:
private val _myType = Impl()
public val myType : Base
get() = _myType
You would code this the same as you did in Java, using two different properties. Unless you are ok with Impl never being specialized in the class. So here are many options:
// if you don't need Impl typed as Impl then just hold it as base
class Test1 {
public val base: Base = Impl()
}
// have both with pointing one reference at the other
class Test2 {
private val _impl = Impl()
public val base: Base = _impl
}
// have both, second one is a getter (no real benefit over Test2)
class Test3 {
private val _impl = Impl()
public val base: Base
get() = _impl
}
// use a function to do basically a cast
class Test4 {
private val _impl = Impl()
public fun asBase(): Base = _impl
}
Or don't worry about this other property, any use of grabbing the Impl can hold it as type Base:
class Test5 {
public val impl: Impl = Impl()
}
// later
val thing: Base = Test5().impl
Maybe you are looking to build this in a way with a common interface to get the base implementation?
open class Base {}
// a common way to get the implementation from within a class
interface Based {
val base: Base
}
class ImplAbc : Base()
class ImplXyz : Base()
class TestAbc : Based {
override val base: Base = ImplAbc()
}
class TestXyz : Based {
private val _impl = ImplXyz()
override val base: Base = _impl
}

Jackson Polymorphism

I have a class Animal and subclass Cat and Dog that extends Animal.
I have a class called Zoo having a variable as List;
i.e.
Class Animal {
String name;
}
Class Cat Extends Animal {
String color;
}
Class Zoo {
List<Animal> animalsInZoo;
public void printAnimalClass()
{
for(Animal a :animalsInZoo)
{
System.out.println(a.getClass.getName());
}
}
}
The object of zoo will have animals that can be objects of Animal or subclass of Animal
Following is sample JSON representation of Object of Zoo class.
{ "animalsInZoo" :
[
{"name":"A"},
{"name": "B","color":"white"}
]
}
I have to convert this into java object in such a way that first animal in list get converted into Object of class Animal and second gets converted into object of Class Cat
You have to do the conversion yourself, supposing you have another animal with same property:
Class Horse Extends Animal {
String color;
}
jackson doesn't know convert {"name": "B","color":"white"} into Cat or Horse.
you may add a property to mark which animal you need to convert to.

Hiding Jackson type info on certain (fields) situations?

The example
Java:
#JsonTypeInfo(
use = JsonTypeInfo.Id.MINIMAL_CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "#type")
public class Pet{
String name;
}
public class Dog extends Pet{}
public class Cat extends Pet{}
public class PetHouse {
List<Pet> pets;
}
public class BarkingData {
int decibels;
Dog dog;
}
JSON Serialization
petHouse = {
pets :
[
{'#type': 'Dog', 'name':'Droopy'},
{'#type': 'Cat', 'name':'Scratchy'},
{'#type': 'Dog', 'name':'Snoopy'}
]
}
barkingData = {
decibels:15,
dog:{'#type':'Dog', 'name':'Droopy'}
}
The Question
Class BarkingData has a field of type Dog (cats don't bark do they). Is it possible to tell Jackson not to include typeInfo for instances where that type can be "hinted" from the declaring field ?
So that the output of Barking data looks like :
barkingData = {
decibels:15,
dog:{'name':'Droopy'}
}
Your idea that you know the dynamic type (actual type) of this field because the static type is Dog and not Animal only works if there are no subclasses of Dog. If you make the Dog class final, then Jackson knows it can safely leave out the type info.
Additionally, you can override Jackson's type info settings, in more complex ways, for fields of static type Dog, by adding a #JsonTypeInfo annotation to the definition of the Dog class.