GWT Autobean without setter method - json

I am using Autobean framework to encode/decode JSON in my GWT application. It works in cases with the interfaces having getters and setters. But is there any way to do it some other way to do this without specifying a setThisCollectionProperty instead using an addToThisCollectionProperty method?
For example, I have an interface IPerson like this:
public interface IPerson {
public String getName();
public void setName(String name);
public int getAge();
public void setAge(int age);
public List<String> getIds();
public void addId(String id);
}
BeanFactory is like this:
public interface BeanFactory extends AutoBeanFactory {
public AutoBean<IPerson> person();
public AutoBean<IPerson> person(IPerson person);
}
and in Person class which implements IPerson,
public class Person implements IPerson {
private String name;
private List<String> ids;
...
public List<String> getIds() {
return ids;
}
public void addId(String id) {
...
ids.add(id);
}
}
It works if the addId(String id) is replaced with setIds(List<String> ids).
Otherwise the following error is shown:
The com.mycompany.jsonsample.beans.IPerson parameterization is not simple, but the person method does not provide a delegate
Is it possible to encode/decode without a set method?

AutoBean manages all getters and setters, and only getters and setters. For any other method, you have to use a category.
Using a category, you could thus implement addId(…) as getIds().add(…), or possibly directly call addIds on the underlying object if the AutoBean is a wrapper.

Related

How to deserialize json to an abstract class in spring-boot

In my Application i have something like this.
public class Question{}
public class MCQ extends Question{}
public class TrueAndFalse Question{}
public class Match Question{}
and in my RestController i have a service that adds question.
#RequestMapping(value = "/game/question/add", method = RequestMethod.POST)
public Question addQuuestion(#RequestParam("gameId") long id, #RequestBody Question question)
But i get an error when i try to call this service as i send json file with different structures one for MCQ, TrueAndFalse and Match.
so is it possible to deserialize the received json to Question abstract class.
And thanks in advance.
You can create a custom deserializer which will create Question instances based on json payload properties.
For example if the Question class looks like this:
public class Question {
private final String name;
#JsonCreator
Question(#JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
}
And sub-class TrueAndFalse:
public class TrueAndFalse extends Question {
private final String description;
#JsonCreator
TrueAndFalse(#JsonProperty("name") String name,
#JsonProperty("description") String description) {
super(name);
this.description = description;
}
public String getDescription() {
return description;
}
}
Then you can create a deserializer, which will create an instance of TrueAndFale sub-class by checking if it has description property:
public class QuestionDeserializer extends JsonDeserializer<Question> {
#Override
public Question deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
ObjectCodec codec = p.getCodec();
JsonNode tree = codec.readTree(p);
if (tree.has("description")) {
return codec.treeToValue(tree, TrueAndFalse.class);
}
// Other types...
throw new UnsupportedOperationException("Cannot deserialize to a known type");
}
}
And afterwards, make sure to register it on the object mapper:
#Configuration
public class ObjectMapperConfiguration {
#Bean
public ObjectMapper objectMapper() {
SimpleModule module = new SimpleModule();
module.addDeserializer(Question.class, new QuestionDeserializer());
return new ObjectMapper().registerModules(module);
}
}

RESTful service with postman

I'm working on a jax-rs RESTful application and I have a service that is supposed to save a JSON objet into a database through JPA. The service class is something like:
#Path("/items")
#Stateless
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ItemsService {
#Inject
protected IItemsLogic itemsServiceLogic;
#POST
public ItemDTO create(ItemDTO item){
return itemsServiceLogic.createItem(item);
}
}
the itemsServiceLogic is just a class that transforms the DTO into another Java class declared as an entity in order to be serialized in a database through JPA.
What happens is that I'm testing the application through the google chrome browser client Postman but when I send a JSON objet into the POST method, the received DTO has no properties so the database doesn't save anything, as all the DTO's properties are null.
I'm using Glassfish 4.0 to host my application and everything with the database works fine. What could be wrong?
The DTO class would be:
#XmlRootElement
public class ItemDTO {
//Id private
Long id;
//item's description
private String description;
//item's name
private String name;
//Setters and getters
public Long getId(){
return id;
}
public String getDescription(){
return description;
}
public String getName(){
return name;
}
public void setId(Long nId){
this.id=nId;
}
public void setDescription(String nDescription){
this.description = nDescription;
}
public void setName(String nName){
this.name=nName;
}
}
And i'm sending the JSON:
{"id":1,"description":"some item","name":"item1"}

RestyGWT Polymorphic Encode/Decode issues when using an interface instead of an abstract class

According to RestyGWT documentation one must use an abstract super class for this to work, for instance, given:
#JsonSubTypes(#Type(value=PersonImpl.class, name="PersonImpl"))
#JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="#class")
public abstract class Person{
public abstract String getName();
public abstract void setName(String name);
}
#JsonTypeName("PersonImpl")
public class PersonImpl extends Person{
private String name;
#Override
public final String getName() {
return name;
}
#Override
public final void setName(String name) {
this.name = name;
}
}
If I use the defined encoder/decoder this would work:
Person personInstance = new PersonImpl();
personInstance.setName("TestName");
PersonCodec codec = GWT.create(PersonCodec.class);
JSONValue json = codec.encode(personInstance);
Im trying to do something quite similar but with a small difference, that is, instead of Person being an abstract class I want it to be an Interface:
#JsonSubTypes(#Type(value=PersonImpl.class, name="PersonImpl"))
#JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="#class")
public interface Person{
public String getName();
public void setName(String name);
}
For some reason this doesn't seem to work, as soon as I do that I start getting Errors when the JsonEncoderDecoder is generated. Has someone been able to achieve this?
Thanks!
Why not define your interface and make your abstract class implement it?

Spring and JacksonJson, serialising different fields with views

In a previous similar question, I asked about, how to serialise two different sets of fields using JacksonJson and Spring.
My use case is the typical Controller mapping with #ResponseBody annotation returning directly a particular object or collections of objects, that are then rendered with JacksonJson whenever the client adds application/json in the Accept header.
I had two answers, the first one suggests to return different interfaces with a different getter list, the second suggests to use Json Views.
I don't have problems to understand the first way, however, for the second, after reading the documentation on JacksonJsonViews, I don't know how to implement it with Spring.
To stay with the example, I would declare three stub classes, inside the class Views:
// View definitions:
public class Views {
public static class Public { }
public static class ExtendedPublic extends PublicView { }
public static class Internal extends ExtendedPublicView { }
}
Then I've to declare the classes mentioned:
public class PublicView { }
public class ExtendedPublicView { }
Why on earth they declare empty static classes and external empty classes, I don't know. I understand that they need a "label", but then the static members of Views would be enough. And it's not that ExtendedPublic extends Public, as it would be logical, but they are in fact totally unrelated.
And finally the bean will specify with annotation the view or list of views:
//changed other classes to String for simplicity and fixed typo
//in classname, the values are hardcoded, just for testing
public class Bean {
// Name is public
#JsonView(Views.Public.class)
String name = "just testing";
// Address semi-public
#JsonView(Views.ExtendedPublic.class)
String address = "address";
// SSN only for internal usage
#JsonView(Views.Internal.class)
String ssn = "32342342";
}
Finally in the Spring Controller, I've to think how to change the original mapping of my test bean:
#RequestMapping(value = "/bean")
#ResponseBody
public final Bean getBean() {
return new Bean();
}
It says to call:
//or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
So I have an ObjectMapper instance coming out of nowhere and an out which is not the servlet typical PrintWriter out = response.getWriter();, but is an instance of JsonGenerator and that can't be obtained with the new operator. So I don't know how to modify the method, here is an incomplete try:
#RequestMapping(value = "/bean")
#ResponseBody
public final Bean getBean() throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonGenerator out; //how to create?
objectMapper.viewWriter(Views.Public.class).writeValue(out, new Bean());
return ??; //what should I return?
}
So I would like to know if anybody had success using JsonView with Spring and how he/she did. The whole concept seems interesting, but the documentation seems lacking, also the example code is missing.
If it's not possible I will just use interfaces extending each others. Sorry for the long question.
Based on the answers by #igbopie and #chrislovecnm, I've put together an annotation driven solution:
#Controller
public class BookService
{
#RequestMapping("/books")
#ResponseView(SummaryView.class)
public #ResponseBody List<Book> getBookSummaries() {}
#RequestMapping("/books/{bookId}")
public #ResponseBody Book getBook(#PathVariable("bookId") Long BookId) {}
}
Where SummaryView is annotated on the Book model like so:
#Data
class Book extends BaseEntity
{
#JsonView(SummaryView.class)
private String title;
#JsonView(SummaryView.class)
private String author;
private String review;
public static interface SummaryView extends BaseView {}
}
#Data
public class BaseEntity
{
#JsonView(BaseView.class)
private Long id;
}
public interface BaseView {}
A custom HandlerMethodReturnValueHandler is then wired into Spring MVC's context to detect the #ResponseView annotation, and apply the Jackson view accordingly.
I've supplied full code over on my blog.
You need to manually wire in the MappingJacksonHttpMessageConverter. In spring 3.1 you are able to use the mvc xml tags like the following:
<mvc:annotation-driven >
<mvc:message-converter>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
It is pretty ugly to not use spring 3.1, it will save you about 20 lines of xml. The mvc:annotation tag does ALOT.
You will need to wire in the object mapper with the correct view writer. I have noticed recently the using a #Configuration class can make complicated wiring like this a lot easier. Use a #Configuration class and create a #Bean with your MappingJacksonHttpMessageConverter, and wire the reference to that bean instead of the MappingJacksonHttpMessageConverter above.
I've manage to solve the problem this way:
Create custom abstract class to contain the json response object:
public abstract AbstractJson<E>{
#JsonView(Views.Public.class)
private E responseObject;
public E getResponseObject() {
return responseObject;
}
public void setResponseObject(E responseObject) {
this.responseObject = responseObject;
}
}
Create a class for each visibility (just to mark the response):
public class PublicJson<E> extends AbstractJson<E> {}
public class ExtendedPublicJson<E> extends AbstractJson<E> {}
public class InternalJson<E> extends AbstractJson<E> {}
Change your method declaration:
#RequestMapping(value = "/bean")
#ResponseBody
public final PublicJson<Bean> getBean() throws JsonGenerationException, JsonMappingException, IOException {
return new PublicJson(new Bean());
}
Create customs MessageConverter:
public class PublicJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public PublicApiResponseMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.Public.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(PublicJson.class)){
return true;
}
return false;
}
}
public class ExtendedPublicJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public ExtendedPublicJsonMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.ExtendedPublic.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(ExtendedPublicJson.class)){
return true;
}
return false;
}
}
public class InternalJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public InternalJsonMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.Internal.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(Internal.class)){
return true;
}
return false;
}
}
Add the following to your xml:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="PublicJsonMessageConverter"></bean>
<bean class="ExtendedPublicJsonMessageConverter"></bean>
<bean class="InternalJsonMessageConverter"></bean>
</mvc:message-converters>
</mvc:annotation-driven>
That's it! I had to update to spring 3.1 but that's all. I use the responseObject to send more info about the json call but you can override more methods of the MessageConverter to be completely transparent. I hope someday spring include an annotation for this.
Hope this helps!

JEE6 - #ApplicationException - #Inject and #PostConstruct not called

I have a problem with #Inject and #PostConstruct method not called in a #ApplicationException annoted class. I'm using Glassfish 3.0.1 with JPA,CDI and EJBs in the service(=ejb)-layer and would like to throw an errorMessage that contains a text in the sessionlanguage.
I have an abstract ExceptionClass
public abstract class LocalizedException extends Exception {
private static final long serialVersionUID = 1L;
String localizedMessage;
//This method should be called as #PostConstruct from the concrete classe
protected void setLocalizedMessage(LocaleHandler localeHandler, String key){
this.setLocalizedMessage(localeHandler, key, new Object());
}
protected void setLocalizedMessage(LocaleHandler localeHandler, String key, Object... args){
localizedMessage = ErrorMessages.getErrorMessage(key,localeHandler.getAktuelleLokale(),args);
}
#Override
public String getMessage() {
return localizedMessage;
}
#Override
public String getLocalizedMessage() {
return localizedMessage;
}}
And a concrete class:
#ApplicationException
public class ConcreteException extends LocalizedException {
private static final long serialVersionUID = 2615388267911318734L;
private int userId;
public ConcreteException(int userId) {
this.userId=userId;
}
public int getUserId() {
return userId;
}
#PostConstruct
#Inject
public void initText(LocaleHandler localeHandler){
setLocalizedMessage(localeHandler, "msgKey");
}
}
The LocaleHandler (=Sessionscoped) should be injected to provide the currentLocale which is used to retrieve an errormessage from a bundle.
The problem is, that the #PostConstruct is never called no matter what I try. I even annoted the concrete class with #Named, used #Inject in the concrete class instead of the abstract, but nothing works. When I call initText() directly I can see (in the debugger), that the LocaleHandler is not injected.
Now I'm asking myself if there is a restriction regarding Exception classes and CDI or did I simply not find the source of the problem !
Do you know the answer ?
thanx in advance
Thomas
The problem is solved.
I simply used the Exception as throw new ConcreteException(), as I'm used to since ages. Excactly this was the problem. Now I inject the ConcreteException into my EJB and throw the containercreated field. This way #Inject and #Postconstruct are working !