Use Json root element only for some classes - java

I'm using dropwizard to create REST API. But I dont understand, how can I configure Jackson to exclude some classes from WRAP_ROOT_VALUE/UNWRAP_ROOT_VALUE features? Right now I get a post request with json body that doesn't include root element name:
{
"identification": "dummyuser",
"password":"dummypass"
}
This should map to java class LoginRequest:
public class LoginRequest {
public String identidication;
public String passwrd;
}
I also get requests for some types that include root element name:
{
"user":{
"id":12345,
"name":"John Doe"
}
}
This should be mapped to:
#JsonRootName("user")
public class User {
...
}
To get root element working I had to include:
environment.getObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true);
environment.getObjectMapper().configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
but now it applies for all classes. This means that whenever login request comes in, server will throw an error because it expects to see root element name.

Use JsonTypeName with JsonTypeInfo instead of JsonRootName:
#JsonTypeName("user")
#JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use= JsonTypeInfo.Id.NAME)
public class User {
...
}
#JsonTypeName
Annotation used for binding logical name that the annotated class has. Used with JsonTypeInfo (and specifically its JsonTypeInfo.use() property) to establish relationship between type names and types.
#JsonTypeInfo
Annotation used for configuring details of if and how type information is used with JSON serialization and deserialization, to preserve information about actual class of Object instances. This is necessarily for polymorphic types, and may also be needed to link abstract declared types and matching concrete implementation.

Related

Ktorm entities as springboot controller parameters

I'm trying to use Ktorm in my new springboot application, and get myself into problem when trying to use Ktorm entities interfaces as springboot controller parameters.
The entity and Controller look like this:
// Controller definition
#RestController
class TaskController() {
#PostMapping
fun addTask(
#RequestBody task: Task
): Long {
// ... do something with `task`
}
}
// Entity definition (unncessary properties are omitted)
interface Task : Entity<Task> {
var id: Long
var title: String
var content: String
companion object : Entity.Factory<Task>()
}
I got this exception once calling function addTask():
[HttpMessageConversionException]
Type definition error: [simple type, class website.smsqo.entity.Task]; nested exception is:
[com.fasterxml.jackson.databind.exc.InvalidDefinitionException]
Cannot construct instance of website.smsqo.entity.Task (no Creators, like default constructor, exist):
abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: (PushbackInputStream); line: 1, column: 1]"
}
(Paramter task is posted from front-end by RequestBody)
I think maybe the reason is that, as an interface, springboot can't find a proper way to initialize Task. However, refactoring it into this is surely not an elegant solution:
#RestController
class TaskController() {
#PostMapping
fun addTask(
id: Long, title: String, content: String // even more fields...
): Long {
val task = Task {
this.id = id
this.title = title
this.content = content
}
// ... do something with `task`
}
}
Any better solution proposed? Thanks for your reply in advance!
Well, it turns out that solution was noted explicitly in documents provided by Ktorm:
// extracted from org.ktorm.entity.Entity
/*
* Besides of JDK serialization, the ktorm-jackson module also supports serializing entities in JSON format. This
* module provides an extension for Jackson, the famous JSON framework in Java word. It supports serializing entity
* objects into JSON format and parsing JSONs as entity objects. More details can be found in its documentation.
*/
Implementing org.ktorm:ktorm-jackson:3.4.1 brings us a Jackson Module, named KtormModule in package org.ktorm.jackson. What we need to do next is applying the module to our springboot application as in class annotated by #Configuration:
#Configuration
class KtormConfig {
#Bean
fun ktormJacksonModule(): Module = KtormModule()
// ... and other configurations if you like
}
And that's it. Such KtormModule will be discovered and applied by jackson on springboot application launches, after which there's no more problem encoding and decoding between json and Ktorm Entities.

Spring Boot Jackson defaultImpl with JsonTypeInfo

I am trying to handle all types of Jackson exceptions that turn up during REST API requests in a Spring Boot application. If something cannot be serialized, JsonMappingException is thrown. I handle this exception, build the field path that cannot be serialized (using exception.getPath) and return this information.
Now, I have some classes that implement the same interface (polymorphism) and have to work with them during a request. This means I also expose them to the REST API and can be included in the request/response body. Here is the interface:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "service",
defaultImpl = DefaultNotificatorPresentation.class,
visible = true
)
#JsonSubTypes({
#JsonSubTypes.Type(value = EmailNotificatorPresentation.class, name = "email")
})
public interface NotificatorPresentation {
String getService();
}
Basically, there are different types of notificators which all have different service (email, sms, etc). This property is used for the #JsonTypeInfo. Everything works as expected until I started testing if JsonMappingException is thrown correctly with the JSON subtypes.
JsonMappingException is thrown for all properties (eg. when malformed) and InvalidTypeIdException when service is not any of the available types (only email at the moment). I would like to tell the user the available options for the service property (when string is given but does not match the available types - email, sms, etc) and that it is malformed when no string is provided (object or array for example).
I came up with a solution that uses defaultImpl of #JsonTypeInfo and uses a custom class with custom validation annotation and ConstraintValidator that handles it.
public class DefaultNotificatorPresentation implements NotificatorPresentation {
// implementation of getService() and validation annotation
}
The annotation has a default message - available services are only email, sms. That way, every time the default implementation is created (always when an invalid service is given by the user) there will be a validation error. This approach works when the property service in the json request is of type string - "not found service" for example.
But when object ({ "example": true }) is set to the service property, the defaultImpl class is created twice. The first instance is given property service "{" (the first character of { "example": true }). The second one service is just null. This creates 2 validation exceptions but must throw JsonMappingException.
Do you have any ideas on how this can be solved? I can even use a totally different approach that handles Jackson polymorphism.

Is there a custom Json Serializer pattern to serialize a property differently including the property's key name?

I have a class that needs to be serialized
public class Abc
{
private long age;
private JaxBElement<Foo> fooWrapper;
// other properties
}
The expected output JSON is
{
"age": 24,
"my_own_key": "my_own_value" // the key should not be "fooWrapper"
A constraint is that the original class Abc cannot be modified since it is generated out of xjc and I don't want to explore custom class using bindings yet.
I have tried custom serializers, bean modifiers etc. for the JaxBElement and all of them allow me to control the serialization. But they work at the VALUE of the property only. They don't allow me to change stuff at the "KEY-VALUE" level. This is the crux of the question. The key is already written out for the property before the custom serializer is invoked to control the value.
E.g. My custom serializer is invoked only after the Jackson system has emitted out the key
"fooWrapper": // now for the value part, let me invoke the custom serializer
So the output JSON always contains the "fooWrapper" key.
{ "fooWrapper": { "any-key": "any-value" } }
// the fooWrapper is already emitted out. That is what needs to be controlled.
My ask is to control the serialization at a higher level, such that both the key and value can be controlled. So when class Abc is being serialized, the fooWrapper property should not be written as a key at all and some custom serializer should be invoked.
Another constraint is that there are several classes like Abc which may have such JaxBElement. It is not known ahead of time. So there needs to be a generic way to attach the custom serializer.
The pseudo ask is really that we be able to attach a custom serializer to any class which has a property that matches a pattern such that the serializer can control the name of the property (or the whole key-value blob) written out.
Also, the problem is not specific to JaxBElement per se. It could be any property. The problem is more about controlled serialization INCLUDING the key being written out.
Maybe you just use the incorrect kind of Serializer. This post, although a bit old should show you how to do what you want with StdSerializer.
This kind of serializer allows you to control both the key and the value.
If you want to control serialisation of key-value pair you need to register custom serialiser not only for JaxBElement<Foo> fooWrapper but also for Abc class to change a key value.
Since it is not a generic solution you can also try to create MixIn class or interface and provide extra configuration:
interface MixInA {
#JsonSerialize(using = JAXBElementJsonSerializer.class)
#JsonProperty("newProperty")
JAXBElement<Foo> getFooWrapper();
}
See also:
What is equivalent code settings for #JSonIgnore annotation?
Make Jackson serializer override specific ignored fields
Downside of this solution is you have to find all types for which you have to register MixIn class or interface. In case fields are different you need to create many different getters or many different MixIn interfaces to cover them all.
So, probably them most flexible solution would be to implement custom com.fasterxml.jackson.databind.AnnotationIntrospector and for given type you can return custom serialiser and custom name. Simple example:
class DynamicJaxbAnnotationIntrospector extends AnnotationIntrospector {
#Override
public Version version() {
return new Version(1, 0, 0, "Dynamic JaxbElement", "your.package", "jackson.dynamic.jaxb");
}
#Override
public Object findSerializer(Annotated am) {
if (am.getRawType() == JAXBElement.class) {
return new JAXBElementJsonSerializer();
}
return super.findSerializer(am);
}
#Override
public PropertyName findNameForSerialization(Annotated a) {
if (a.getRawType() == JAXBElement.class) {
return new PropertyName("newProperty");
}
return super.findNameForSerialization(a);
}
}
See also below article how to use it:
How to serialise Enums as both Object Shape and default string?

accept argument with object which has a specific annotation

PROBLEM
If I generate a jaxb object from an XML root element named 'Message' it produces the following class:
#XmlRootElement(name = "Message")
public class Message{
...
}
I have a method with the following definition:
public void doSomething(Object rootElement) {
...
}
This method is part of a framework used by 30+ developers. The method only accepts a class with the annotation #XmlRootElement. Developers sometimes miss this crucial javadoc and pass a non #XmlRootElement object to the method giving errors during runtime. As the generated class has no superclass, no interfaces, etc I have to accept an Object as argument.
QUESTION
Is it not possible to enforce this check compile time ?
ALREADY TRIED
I've looked into (via jaxb binding file) letting generated classes implement an interface and then accept this interface as argument, however it cannot (in an automated manner) be applied to only the root element class.
We have several hundred different XML Schema files from which we generated in an automated manner.

Mapping JSON response to an abstract type in JAVA rest Service

I am working with an ecommerce company and I am integrating with 3 different payment gateways. All of them require a callbackurl to post back the transaction status.
I have defined a resource to store the status of the transaction
http://www.api.com/api/users/{userid}/order/{orderId}/payments/{paymentModeId}/paymentStatus
I have defined an interface called IPaymentStatusResponse and have created 3 implementations. Based on the paymentModeId in the uri path, appropriate implementation will be picked up to persist the transaction status.
eg: callback url for three different gatewyas will look like this
payment mode 1 - paytm, payment mode 2 - payu ,payment mode 3- cc avenue.
http://www.api.com/api/users/300/order/501/payments/1/paymentStatus
http://www.api.com/api/users/300/order/501/payments/2/paymentStatus
http://www.api.com/api/users/300/order/501/payments/3/paymentStatus
Method signature
public void createPaymentStatus(
#PathParam("paymentModeId") int paymentModeId,
IPaymentStatusResponse response) throws MyAppException {
paymentServiceImpl.createPaymentResponse(response, paymentModeId);
}
Is this the correct way to approach this?
When I did a HTTP post I get the following error:
Can not construct instance of com.myapp.dto.payments.IPaymentStatusResponse, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#1ee3ab21; line: 1, column: 1]
Other option , I ll have to define different end points for all the different gateways and map the response object.
UPDATE:
A good explanation can be found here
http://programmerbruce.blogspot.in/2011/05/deserialize-json-with-jackson-into.html
It needs a type element in the response json to pick the concrete class.
Sample json with type mentioned and configuration of my interface. This works. But not sure how to handle this because the response json is not under my control and it comes from the payment gateway providers
{
"MID":"abc",
"TXNID":"T123",
"ORDERID":"100",
"BANKTXNID":"B123",
"TXNAMOUNT":"1",
"CURRENCY":"INR",
"STATUS":"TXN_SUCCESS",
"RESPCODE":"01",
"RESPMSG":"Txn Success",
"TXNDATE":"2015-12-14 02:10:29.742447",
"GATEWAYNAME":"ICICI",
"BANKNAME":"ICICI",
"PAYMENTMODE":"CC",
"type":"PayTMPaymentResponse",
"CHECKSUMHASH":"ggg"
}
Interface
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({ #Type(value = PayTMPaymentResponse.class, name = "PayTMPaymentResponse") })
public interface IPaymentStatusResponse {
}
Can this be implemented with some query or path parameter ?
Spring/Jackson is not able to deserialize automatically.
You'll have to provide custom deserializer for a given type.
I think it should be easier to do one POJO for shared information and 3 strategies.
it should help in case if you're using jackson. I think other libs have something similar to that.
http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

Categories