I dont understand what is wrong in birectional relationship deserializing :
Serialized Registration is :
{
uuid: "d3372f25-ac70-4735-b756-7894977142b2",
payment: [{
"uuid":"15363b3f-7057-4658-3f63-3f3f695d7019",
"type":0,
"amount":10
"registration":"d3372f25-ac70-4735-b756-7894977142b2"
},
{
"uuid":"ee9fb135-2b21-48f4-a1ee-2f71f09f44ab",
"type":0,
"amount":5
"registration":"d3372f25-ac70-4735-b756-7894977142b2"
}]
}
Java is :
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "uuid", scope = Registration.class)
public class Registration implements Serializable {}
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "uuid", scope = Payment.class)
public class Payment implements Serializable {}
Could not read document: Already had POJO for id (java.util.UUID) [[ObjectId: key=d3372f25-ac70-4735-b756-7894977142b2, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=com.mezoo.tdc.model.Registration]] (through reference chain: com.mezoo.tdc.model.Registration["payment"]->java.util.ArrayList[0]->com.mezoo.tdc.model.Payment["registration"]->com.mezoo.tdc.model.Registration["uuid"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id (java.util.UUID) [[ObjectId: key=d3372f25-ac70-4735-b756-7894977142b2, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=com.mezoo.tdc.model.Registration]] (through reference chain: com.mezoo.tdc.model.Registration["payment"]->java.util.ArrayList[0]->com.mezoo.tdc.model.Payment["registration"]->com.mezoo.tdc.model.Registration["uuid"])"
Somebody can explain to me why deserializing doesnt works please ?
Related
Recently I try to learn Kotlin by writing a Kotlin + Exposed demo.
Common steps using Java + MyBatis would be: creating Java Bean class, mapper class, service class and service implementation class
#Data class UserBean { String username; String password; }
#Mapper interface UserMapper extends BaseMapper<UserBean> {}
interface UserService extends BaseService<UserBean> {}
#Service UserSerivceImpl extends BaseServiceImpl<UserBean> implements UserService {}
Then those Java Bean classes are used in any other parts of system for database IO and JSON serialization.
// create instance and convert to JSON
var user = new UserBean();
user.setUsername("username");
user.setPassword("password");
var om = new ObjectMapper();
var json = om.valueToTree(user);
Following official doc of Exposed DAO API, I create those classes:
class User(id : EntityID<Int>) : IntEntity(id)
{
companion object : IntEntityClass<User>(Users)
var username by Users.username
var password by Users.password
}
object Users : IntIdTable()
{
val username = varchar("username", 64)
val password = varchar("password", 64)
}
When performing database IO, User.all() and User.new { } api work well. But creating instance directly would throw an exception:
// get data from JSON or somewhere else
val username = ...
val password = ...
// trying to create instance
val id = EntityID(-1, User.table) // trying to create an empty ID. I don't know if this is allowed
val user = User(id)
user.username = username // this would throw exception
user.password = password
Caused by: java.lang.IllegalStateException: Property klass should be initialized before get.
at kotlin.properties.NotNullVar.getValue(Delegates.kt:62)
at org.jetbrains.exposed.dao.Entity.getKlass(Entity.kt:34)
at org.jetbrains.exposed.dao.Entity.setValue(Entity.kt:198)
Post here says Exposed does not allow creating DAO objects by yourself. So is there a easy way to re-use those DAO classes for JSON serialization or transfering data between parts of program? Should I create a DTO class with identical data fields?
Exposed Entities are stateful objects. You shouldn't serialize them directly. Instead, as you mentioned, you should use a simple data class with serialization annotations relevant to you.
For example:
class User(id : EntityID<Int>) : IntEntity(id)
{
companion object : IntEntityClass<User>(Users)
var username by Users.username
var password by Users.password
fun asResponse(): UserResponse {
return UserResponse(username, password)
}
}
#Serializable
data class UserResponse(val username: String, val password: String)
Everyone following is my json response:
{
"requestResponse": {
"status": 1,
"result": true,
"msg": "Success"
},
"userId": 5504
}
And following is my Base Response class:
class BaseResponses<T>{
lateinit var requestResponse: RequestResponse
}
and following are my User data class parameters.
data class User(val userId:Int)
And below as implementation:
#POST(ApiUrls.CREATE_USER)
fun createUser(#Body body: CreateUser): Single<BaseResponses<User>>
my question is that how can I access T type which is User in the Base class would highly appreciate the help.
Thanks
You don't need a genetic type - you need to inherit the properties.
data class BaseResponses { // Remove T, it's doing nothing
lateinit var requestResponse: RequestResponse
}
// Extend base class to inherit common `requestResponse` field
data class User(val userId:Int) : BaseResponses()
// User now will contain requestResponse object
#POST(ApiUrls.CREATE_USER)
fun createUser(#Body body: CreateUser): Single<User>
I might be understanding you wrong, you just want to re-use the RequestResponse class since it is generic and will be common in all your APIs. So just have it as a parameter in User data class.
So it will be like this
data class User(
val requestResponse: RequestResponse,
val userId: Int
)
Now you can simply access it directly from User object. You can even go a step further and assign it default values like this
data class User(
val requestResponse: RequestResponse = RequestResponse(),
val userId: Int = 0
)
data class RequestResponse(
val msg: String = "",
val result: Boolean = false,
val status: Int = 0
)
I have a project with Kotlin and Springboot.
In this project an entity have this following fields: id, name and parent.
What i want is to only get the id and the name. So i made a projection interface view for that entity.
This is my entity:
#Entity
data class Keyword(
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
var id: UUID? = null,
#Column(columnDefinition = "BINARY(16)")
var parent: UUID? = null,
#NotBlank
var name: String? = null
)
Repository:
#Repository
interface KeywordRepository: JpaRepository<Keyword, UUID> {
#Query(value = "SELECT keyword.id, keyword.parent, keyword.name FROM keyword LEFT JOIN project_keyword ON project_keyword.keyword_id = keyword.id WHERE project_keyword.project_id LIKE :id", nativeQuery = true)
fun findAllKeywordsByProjectId(id: UUID): MutableList<KeyWordView>
}
Service:
#Service
class KeywordService (
private val projectService: ProjectService,
private val keywordRepository: KeywordRepository
) {
fun getKeywordsByProjectId(id: UUID): MutableList<KeyWordView> {
projectService.checkIfProjectExistsById(id)
return keywordRepository.findAllKeywordsByProjectId(id).toMutableList()
}
}
My projection interface class:
interface KeyWordView {
val id: UUID
val name: String?
}
When i call this endpoint via controller class. I get this output:
"list": [
{
"name": "MARINE MICROBIOTA",
"id": "0,31,-36,77,29,123,66,-25,-127,-43,-31,83,104,-90,47,10"
}
]
But if i change the val id: String to val id: UUID in my KeywordView interface, i get this following error:
{
"code": 500,
"data": null,
"message": "Could not write JSON: Projection type must be an interface!; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Projection type must be an interface! (through reference chain: no.inmeta.ris.util.response.ResponseDto[\"data\"]->no.inmeta.ris.util.pagination.PageDto[\"list\"]->java.util.ArrayList[0]->com.sun.proxy.$Proxy199[\"id\"])",
"status": "FAIL"
}
Anyone know how to solve this problem? I want to receive the UUID as UUID not with the strange format.
Thank you!
I guess the problem is the UUID Class. As your entity states you defined dir hibernate that the column in db is a binary(16). And i suppose using this UUID Type in the Interface projection does not Work since there is no valid mapping information (how to transform the data coming as binary with a length of 16 bytes to the UUID Class). So I assume you have to change the Type of your column or you have to use string and write a function to Transform that String.
Another possibility is to create a second entity for that table with Just the two cols you want. And Just use that second dao as your are using yiur projection.
I have an issue with object references to abstract classes and JSON serialization and deserialization. The abstracted issue looks like this:
I have a graph consisting of nodes and edges. Each edge connects two nodes. Nodes can be of flavor red and green. Therefore, there is an abstract class Node and two derived classes RedNode and GreenNode. A Node takes an id (#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")):
#JsonSubTypes({
#JsonSubTypes.Type(value = GreenNode.class, name = "GreenNode"),
#JsonSubTypes.Type(value = RedNode.class, name = "RedNode")
})
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public abstract class Node {
public String id;
}
public class RedNode extends Node {
// ...
}
public class GreenNode extends Node {
// ...
}
An Edge has a source and a target of type Node, which are serialized as references (#JsonIdentityReference(alwaysAsId = true)):
public class Edge {
#JsonIdentityReference(alwaysAsId = true)
public Node source;
#JsonIdentityReference(alwaysAsId = true)
public Node target;
}
The graph is defined as follows:
public class Graph {
public List<GreenNode> greenNodes = new ArrayList();
public List<RedNode> redNodes = new ArrayList();
public List<Edge> edges = new ArrayList();
}
An example JSON looks as follows:
{
"greenNodes" : [ {
"id" : "g",
"content" : "green g",
"greenProperty" : "green"
} ],
"redNodes" : [ {
"id" : "r",
"content" : "red r",
"redProperty" : "red"
} ],
"edges" : [ {
"source" : "g",
"target" : "r"
} ]
}
Using an ObjectMapper cannot read this:
Can not construct instance of com.github.koppor.jsonidentityissue.model.Node: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
The error location is "line: 13, column: 16". Thus, it is hit at the id of the edge. The nodes themselves are properly serialized.
A workaround is to add type information in the json:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
public abstract class Node {
Then, everything works:
{
"greenNodes" : [ {
"id" : "g",
"type" : "GreenNode",
"content" : "green g",
"greenProperty" : "green"
} ],
"redNodes" : [ {
"id" : "r",
"type" : "RedNode",
"content" : "red r",
"redProperty" : "red"
} ],
"edges" : [ {
"source" : "g",
"target" : "r"
} ]
}
Then, everything works.
Is it really necessary to include type information in the referenced objects to have the reference working? Without the type information, a graph with red and green nodes (and no edges) can be loaded. After an edge comes in, it can't. However, the JSON of an edge contains an id only. The referenced objects are already parsed.
I really like to get rid off the #JsonTypeInfo annotation. Is there a way to have a clean JSON?
The full example is made available at https://github.com/koppor/jackson-jsonidentityreference-issue/tree/issue.
The current solution is to include fake type information. Full code at https://github.com/koppor/jackson-jsonidentityreference-issue.
The Node gets an existing property type, which is not written:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type")
public abstract class Node {
#JsonIgnore
public abstract String getType();
}
Each subclass specifies itself as defaultImpl and provides an implementation of getType:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type",
defaultImpl=GreenNode.class)
public class GreenNode extends Node {
#Override
public String getType() {
return "GreeNode";
}
}
This way, the JSON remains clean, but Jackson can resolve the id reference without any issues.
I have JSON that is like this:
{
"apps": [
{
"id": "1",
...
},
{
"id": "2",
...
}
]
}
And for example say the Application class looks like this
data class Application(
val id: String
)
I want to deserialize the JSON into a List<Application>, where each {...} is an Application. I was hoping to do this without having to create a wrapper class like Applications, annotating it with #JsonRootName, and then enabling DeserializationFeature.UNWRAP_ROOT_VALUE. The end goal is to have a Retrofit interface that has something like:
#GET("api/apps")
fun listApplications(): Call<List<Application>>
I tried to implement a simple JsonDeserializer (could probably be optimized):
class ApplicationListDeserializer
: JsonDeserializer<List<Application>>() {
companion object {
private val COLLECTION_TYPE: CollectionType = TypeFactory.defaultInstance()
.constructCollectionType(List::class.java, Application::class.java)
}
override fun deserialize(parser: JsonParser, context: DeserializationContext): List<Application> {
val mapper = ObjectMapper()
val node: JsonNode = parser.codec.readTree(parser)
val collectionReader = mapper.readerFor(COLLECTION_TYPE)
return collectionReader.readValue(node.get("apps"))
}
}
I don't see any way to register this deserializer for this specific type. I tried this:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.readValue
val objectMapper = ObjectMapper().registerModule(KotlinModule())
val module = SimpleModule()
module.addDeserializer(List::class.java, ApplicationListDeserializer())
objectMapper.registerModule(module)
val applications: List<Application> = objectMapper.readValue("""{
"apps": [
{
"id": "/api-catalog-backend"
}
]
}""")
But that fails with:
Can not construct instance of Application: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: N/A; line: -1, column: -1] (through reference chain: java.util.ArrayList[0])" type="com.fasterxml.jackson.databind.JsonMappingException"><![CDATA[com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of Application: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: N/A; line: -1, column: -1] (through reference chain: java.util.ArrayList[0])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1456)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012)
I am using the following dependency versions:
compile("com.fasterxml.jackson.core", "jackson-core", "2.8.6")
compile("com.fasterxml.jackson.module", "jackson-module-kotlin", "2.8.4")
compile(kotlinModule("stdlib", "1.1-M03"))
compile(kotlinModule("reflect", "1.1-M03"))
How do I configure the deserialization to work correctly with a List<Application>?