Jackson : #JsonTypeId is not getting serialized properly when nested - jackson

I have a Zoo class which can contain animal of different types (Dog, ...) and has an animalType annotated with #JsonTypeId. The Dog class in turn can contain leash of different types (RopeLeash, ...) and has leashType annotated with #JsonTypeId. When i serialize the Zoo class using below method then the leashType gets set for animalType as well:
String zooJson = objectMapper.writeValueAsString(zoo);
Output is:
{"animal":{"leash":{"leashColor":"RED"},"leashType":"ROPE"},"animalType":"ROPE"}
Classes:
public class Zoo {
#JsonTypeId
private AnimalType animalType;
private Animal animal;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "animalType"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = Cat.class, name = "CAT"),
#JsonSubTypes.Type(value = Dog.class, name = "DOG")
})
public void setAnimal(Animal animal) {
this.animal = animal;
}
//Other getters and setters
}
public class Dog extends Animal {
#JsonTypeId
private LeashType leashType;
private Leash leash;
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "leashType"
)
#JsonSubTypes({
#JsonSubTypes.Type(value = RopeLeash.class, name = "ROPE")
})
public void setLeash(Leash leash) {
this.leash = leash;
}
//Other getters and setters
}
public class RopeLeash extends Leash {
private String leashColor;
//Getter and setter for leashColor
}
Is there something wrong in my annotation usage?

It seems that Jackson does not support multi-level IDs. Posted this question in the Jackson site and here is the response:
Multi-level type ids are not supported; a single id is required. No
support for multiple levels are planned to be used.
Link: https://github.com/FasterXML/jackson-databind/issues/1462

Related

Kotlin sealed class

I have below class, I would like to make this class a sealed class. Can you please help me as I am new to Kotlin.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes(
JsonSubTypes.Type(value = A:class, name = "PIZZA"),
JsonSubTypes.Type(value = B::class, name = "DONUT"),
JsonSubTypes.Type(value = C::class, name = "ICECREAM"),
JsonSubTypes.Type(value = D::class, name = "CHOCOLATE"),
)
open class food (var type: foodType, var quantity : String) {
open val taste : String=""
}
How to make this a sealed class perhaps a subclass of a sealed class, and how to instantiate it?
The foodType is enum class
enum class foodType {
PIZZA,
DONUT,
ICECREAM,
CHOCOLATE
}
I have the following based on the other post, but I am confused on passing the right parameters. Can someone help me understand what parameter I need to pass??
sealed class food (var type: foodType, var quantity: String) {
class favFood(taste: String): food(?, ?)
}
What is a sealed class ?
When you create a sealed class, you only allow the implementations you
created, just like for an enum (Only the constants you added are allowed). Once the module is compiled, you can't add any additional implementation anymore (in opposite to an open class).
Here is the link to the Kotlin documentation about sealed classes : https://kotlinlang.org/docs/sealed-classes.html
Sealed classes are interesting when you want to restrict the implementations
to a strict proposition. It can be the case with your use case, to restrict the jsonSubTypes you allow (others wouldn't be mapped).
How to transform an open class to a sealed class ?
So to transform your open class to a sealed class, you generally just need to change the keyword open to sealed. However, you also need to understand how the inheritance mechanism work with sealed classes.
For your example
With JsonSubType, you just need to map the property type to an implementation of your sealed class using a constant of your choice.
Also, you have to provide the values to your sealed class' properties when you extend it, so when you create your implementations.
In the next example, you can find how to give a value to your sealed class properties and what will be the result when you map it to json using JSonSubType :
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes(
JsonSubTypes.Type(value = Pizza::class, name = "Pizza"),
JsonSubTypes.Type(value = Donut::class, name = "DonutDesert"), // As you can see, name is a value you give, not always need to be the class name
JsonSubTypes.Type(value = IceCream::class, name = "IceCream")
)
sealed class Food(val taste: String)
class Pizza(val size: PizzaSize, taste: String) : Food(taste) {
enum class PizzaSize {
SMALL,
MEDIUM,
LARGE
}
}
class Donut(val glaze: String, taste: String) : Food(taste)
class IceCream(val servings: Int, taste: String) : Food(taste)
class Basket(foods: List<Food>)
/* If you map a Basket to JSON, it will give you this :
{ foods: [
{ "type": "Pizza", "size": "MEDIUM", "taste": "Hawaii" },
{ "type": "DonutDesert", "glaze": "Sugar & Marshmallows", "taste" : "chocolate"},
{ "type": "IceCream", "servings": 3, "taste": "Strawberry" }
]}
*/

Property in class header vs in class body

I have an abstract class entity.
abstract class AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "id")
#SequenceGenerator(name = "id", sequenceName = "id_sequence", allocationSize = 1000)
var id: Long? = null
#Version
private var version: Int = 0
#NotNull
var createdDate = ZonedDateTime.now()!!
}
And I have a class(javax.persistence Entity ) that inherits from AbstarctEntiy
#Entity
#Table(schema = "query")
data class Query(
var name: String?,
) : AbstractEntity()
Is there any difference between using users in class header and in class body as in the following two codes?
1
#Entity
#Table(schema = "query")
data class Query(
var name: String?,
#OneToMany(mappedBy = "id", fetch = FetchType.EAGER)
var users : List<Username> = mutableListOf()
) : AbstractEntity()
2
#Entity
#Table(schema = "query")
data class Query(
var name: String?,
) : AbstractEntity() {
#OneToMany(mappedBy = "id", fetch = FetchType.EAGER)
var users : List<Username> = mutableListOf()
}
There is a difference between passing an item through the constructor, and setting it as a property because you are using a data class to hold those.
While in example 1 and 2 Kotlin is generating a getter and a setter for both the user and name fields, main benefits of using a data class only get leveraged for items passed through the constructor.
In Example 1, because it's a data class Kotlin overrides the 'copy', 'toString', 'hashCode' and 'equals' classes for BOTH the properties you're passing into the constructor. So just as an example, the 'toString' function would look like so in the decompiled java code
#NotNull
public String toString() {
return "Query(name=" + this.name + ", users=" + this.users + ")";
}
In Example 2, you only get this benefit for the name property you are passing into the constructor, but not for the user list. In this case, the toString() and all the other functions I mentioned would only take into consideration name
public String toString() {
return "Query1(name=" + this.name + ")";
}
This is true for all the rest of copy() hashCode(), and equals()
If you care about Kotlin handling these for both user and name then pass both through in the constructor. Otherwise, it doesn't matter.

How to assign new value if you setting the setter private in kotlin?

I am new in kotlin and not able to understand how the getter and setter behave in kotlin, so if I set the setter to private. Then what is the way of updating the value.
package foo
class Person() {
var name: String = "defaultValue"
private set
}
If you set your setter to be private, then this setter will be accessible only from within its class. In other words you can use normal assignment even when your setter is private but only from within the class.
class Person() {
var name: String = "defaultValue"
private set
fun foo(bar: String) {
name = bar // name can be set here
}
}
fun main(args: Array<String>) {
Person().name = "foo" // error. Name can be accessed but can not be modified here as its setter is private.
}
For more information check the Kotlin's Visibility documentation.
the kotlin code above will be transform to java code by kotlin compiler more like as below:
package foo;
public final class Person{
private String name = "defaultValue";
public final String getName(){
return name;
}
private final void setName(String name){
this.name=name;
}
}
which means you can only change the name field in the Person class. another situation is if you want to modify the name property with private setter out of the Person class. you can using java reflection instead, for example:
val person = Person();
val field = Person::name.javaField!!.apply { isAccessible = true }
field.set(person, "bob")
println(person.name)// "bob"
val setter = Person::class.java.getDeclaredMethod("setName", String::class.java)!!
.apply {
isAccessible = true
}
setter.invoke(person, "john")
println(person.name)// "john"

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
}

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.