Why annotation #JsonValue does not work in Jersey? - java

I have RESTful service based on Jersey framework.
I need to serialize and deserialize some object which include enum fields.
I wrote two methods and annotated they with #JsonValue and #JsonCreator. But Jersey doesn't show enum fields?
My object with enum:
#XmlRootElement
public class RecipientWrapper{
#ApiModelProperty(hidden = true)
private Recipient recipient;
private String name;
private String address;
private RecipientType type;
public RecipientWrapper(){}
public RecipientWrapper(String name, String address, Message.RecipientType type) {
recipient = new Recipient(name, address, type);
}
#XmlTransient
public Recipient getRecipient(){
if (recipient == null && name != null && address != null){
if (type != null){
recipient = new Recipient(name, address, type.toMsgRecipientTypeValue());
}
else{
recipient = new Recipient(name, address, Message.RecipientType.TO);
}
}
return recipient;
}
public void setRecipient(Recipient recipient){
this.recipient = recipient;
}
public String getName(){
return recipient.getName();
}
public String getAddress(){
return recipient.getAddress();
}
public Message.RecipientType getType(){
return recipient.getType();
}
public void setName(String name) {
this.name = name;
}
public void setAddress(String address) {
this.address = address;
}
public void setType(RecipientType type) {
this.type = type;
}
public enum RecipientType {
TO,
CC,
BCC;
private static Map<String, RecipientType> nameMap = new HashMap<String, RecipientType>();
private static Map<RecipientType, Message.RecipientType> msgRecipientType = new HashMap<RecipientType, Message.RecipientType>();
static{
nameMap.put("TO", TO);
nameMap.put("CC", CC);
nameMap.put("BCC", BCC);
msgRecipientType.put(TO, Message.RecipientType.TO);
msgRecipientType.put(CC, Message.RecipientType.CC);
msgRecipientType.put(BCC, Message.RecipientType.BCC);
}
#JsonCreator
public static RecipientType forValue(String value){
return nameMap.get(StringUtils.upperCase(value));
}
#JsonValue
public String toValue(){
for (Map.Entry<String, RecipientType> entry: nameMap.entrySet()){
if (entry.getValue() == this){
return entry.getKey();
}
}
return null;
}
public Message.RecipientType toMsgRecipientTypeValue(){
for (Map.Entry<RecipientType, Message.RecipientType> entry: msgRecipientType.entrySet()){
if (entry.getKey() == this){
return entry.getValue();
}
}
return null;
}
}
}
And gradle dependencies:
compile 'javax.ws.rs:jsr311-api:1.1.1'
compile 'com.sun.jersey:jersey-server:1.18.1'
compile 'com.sun.jersey:jersey-servlet:1.18.1'
compile 'com.sun.jersey:jersey-bundle:1.18.1'
compile 'io.swagger:swagger-jersey-jaxrs:1.5.0'
compile 'org.antlr:stringtemplate:4.0.2'
compile 'org.freemarker:freemarker:2.3.22'
compile 'org.apache.velocity:velocity:1.7'
compile 'org.codemonkey.simplejavamail:simple-java-mail:2.2'
compile 'org.slf4j:slf4j-simple:1.7.12'
compile 'org.slf4j:slf4j-api:1.7.12'
compile 'org.codehaus.jackson:jackson-mapper-asl:1.9.13'
JSON Output:
{
"address": "mymail#gmail.com",
"name": "My name"
}
Why doesn't Jersey show enum fields?

The problem is the #JsonValue annotation. If you remove it - it will work.
Example:
public class A {
private String type;
private RecipientType recipientType;
public A(String type, RecipientType recipientType) {
this.type = type;
this.recipientType = recipientType;
}
public String getType() {
return type;
}
public RecipientType getRecipientType() {
return recipientType;
}
public enum RecipientType {
TO,
CC,
BCC;
}
}
When serializing it I get
{"type":"a","recipientType":"BCC"}
Jackson by itself knows to handle enums, you don't need to anything special for it. Add the #JsonValue just ruined it.

Related

How to specify a default assembler in seedstack?

I'm using the 19.11 version of seedstack, and I want to use the FluentAssembler assembler to convert an aggregate List into a DTO List.
I'm getting the following error when I call the fluentAssembler.assemble method :
org.seedstack.business.internal.BusinessException: [BUSINESS] Unable to find assembler
Description
-----------
No assembler was found to assemble 'com.inetpsa.svr.domain.model.customer.Customer(Customer.java:1)' to
'com.inetpsa.svr.interfaces.rest.customer.CustomerRepresentation(CustomerRepresentation.java:1)'.
Fix
---
Make sure that an assembler without qualifier exists. If you want to use a qualified assembler (like a default
assembler), specify its qualifier.
I don't know howto specify the qualifier, I'd like to use a default model mapper...
Here is The Resource code :
#Path("customers")
public class CustomerResource {
#Inject
private FluentAssembler fluentAssembler;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<CustomerRepresentation> listAllCustomers() {
List<Customer> customerList = fetchAllCustomers();
return fluentAssembler.assemble(customerList).toListOf(CustomerRepresentation.class);
}
/**
* Test method - Should be replaced by a repository
* #return List<Customer> all customers
*/
private List<Customer> fetchAllCustomers(){
List<Customer> customerList = new ArrayList<>();
customerList.add(buildCustomer("005","Edward Teach","edward.teach#pirates.org"));
customerList.add(buildCustomer("006","Olivier Levasseur","olivier.levasseur#pirates.org"));
customerList.add(buildCustomer("007","James Bond","james.bond#mi6.uk"));
return customerList;
}
private Customer buildCustomer(String id, String name, String mail){
Customer result = new Customer(id);
result.updateNameAndMail(name, mail);
return result;
}
}
The aggregate :
public class Customer extends BaseAggregateRoot<String> {
#Identity
private String identifier;
private String name;
private String mail;
public Customer(String identifier){
this.identifier=identifier;
}
public void updateNameAndMail(String name, String mail){
if(StringUtils.isBlank(name)){
throw new IllegalArgumentException("Name can't be blank");
}
if(StringUtils.isBlank(mail)){
throw new IllegalArgumentException("Mail can't be blank");
}
this.name=name;
this.mail=mail;
}
public String getIdentifier() {
return identifier;
}
public String getName() {
return name;
}
public String getMail() {
return mail;
}
}
And the DTO :
#DtoOf(Customer.class)
public class CustomerRepresentation {
private String identifier;
private String name;
private String mail;
/**
* Required public no parameters constructor
*/
public CustomerRepresentation(){}
public CustomerRepresentation(String identifier, String name, String mail){
}
#AggregateId
public String getIdentifier() {
return identifier;
}
public String getMail() {
return mail;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public void setMail(String mail) {
this.mail = mail;
}
}
FluentAssembler only takes care of matching a Dto with an Assembler, but does not provide a default implementation of an Assembler by itself.
You have 2 Options to provide a Default Assembler.
Build a class that implements Asselmber
Include an addon that provides that Default Assember for you (As stated on the docs)

Deserializing generic java object returns LinkedTreeMap

I have a generic Java Message object that's represented by the following json string:
{
"type": "Example",
"processTime": 3.4,
"payload":
{
"id": "someString",
"type": "anotherString",
"message": "yetAnotherString"
}
}
The Java Message object is generic. I also have an object called Event. When trying to convert the json into a Message<Event> object using gson, a Message object is returned with the correct json values, but the nested generic object is somehow returned as a "LinkedTreeMap" object instead of an Event object. I know this has something to do with type erasure, but I still can't seem to figure out how to return a Message<Event> from the json.
This is my main():
public class App {
public static void main(String[] args) {
//The json string to convert into a "Message<Event>" object
String jsonString = "{\"type\":\"Example\",\"processTime\":3.4,\"payload\":{\"id\":\"someString\",\"type\":\"anotherString\",\"message\":\"yetAnotherString\"}}";
Message<Event> message = new Message<Event>();
message = message.convertJsonToObject(jsonString, Event.class);
System.out.println(message.getClass().getName()); //returns as a "Message" class -- as expected
System.out.println(message.getPayload().getClass().getName()); //returns as a "LinkedTreeMap" instead of an "Event" object
}
}
Message class:
public class Message<T> {
private String type;
private double processTime;
private T payload;
public Message(String type, double processTime, T payload) {
this.type = type;
this.processTime = processTime;
this.payload = payload;
}
public Message() {
type = null;
processTime = 0;
payload = null;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public double getProcessTime() {
return processTime;
}
public void setProcessTime(double processTime) {
this.processTime = processTime;
}
public T getPayload() {
return payload;
}
public void setPayload(T payload) {
this.payload = payload;
}
public Message<T> convertJsonToObject(String jsonString, Class<T> classType) {
GsonBuilder gson = new GsonBuilder();
Type collectionType = new TypeToken<Message<T>>() {}.getType();
Message<T> myMessage = gson.create().fromJson(jsonString, collectionType);
return myMessage;
}
#Override
public String toString() {
return new Gson().toJson(this);
}
}
Event class:
public class Event {
private String id;
private String type;
private String message;
public Event(String id, String type, String message) {
this.id = id;
this.type = type;
this.message = message;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
#Override
public String toString() {
return new Gson().toJson(this);
}
}

Java android realm check if objects is exist (check two )

This is my object :
public class ObjectsInGroupRealm extends RealmObject {
#PrimaryKey
private Long id;
private String name;
private String groupName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
}
And when I create a new object I want to check if object is exist this same name and this same groupName . A object name could be in few groups. This is my code how I save a objects :
public static void saveObjectsInGroup(ArrayList<String> objects, String groupName , Realm realm){
for(String object : objects){
ObjectsInGroupRealm objectsInGroupRealm = new ObjectsInGroupRealm();
Long key;
try {
key = (Long) realm.where(ObjectsInGroupRealm.class).max("id") + 1;
} catch (NullPointerException ex) {
key = 0L; // when there is no object in the database yet
}
objectsInGroupRealm.setId(key);
objectsInGroupRealm.setName(object);
objectsInGroupRealm.setGroupName(groupName);
realm.beginTransaction();
realm.copyToRealm(objectsInGroupRealm);
realm.commitTransaction();
}
}
So the easiest way is doing a query and checking if the returned Object is null:
ObjectsInGroupRealm object = realm.where(ObjectsInGroupRealm.class)
.equalTo("name", name)
.equalTo("groupName", groupName)
.findFirst();
if(object == null){
//add new object
} else {
//handle object already existing
}

Restrict mutable object inside immutable object Java

I am learning about immutable Objects. I am trying this code
public final class ImmutableObject {
private final String name;
private final NormalObject obj = new NormalObject();
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
obj.setName(name);
}
public NormalObject getObj() {
NormalObject tempObj = obj;
return tempObj;
}
}
public class NormalObject {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I want to restrict the calling class from changing the value of name variable of NormalObject
But the following code changes the value
ImmutableObject obj = new ImmutableObject("Siddle");
System.out.println(obj.getObj().getName()); //prints Siddle
obj.getObj().setName("Kelly");
System.out.println(obj.getObj().getName()); //prints Kelly
How to restrict it?
For an object to be immutable, all of its properties must be immutable. Its state must not be changeable.
To do that, you have to put an immutable facade on NormalObject, you can't directly return a NormalObject. The method that returns it will also need a different return type, you can't return NormalObject but actually return something that doesn't behave like a NormalObject.
E.g.:
public final class ImmutableObject {
private final String name;
private final NormalObject obj = new NormalObject();
private final ImmutableNormalObject objFacade = new ImmutableNormalObject(obj);
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
obj.setName(name);
}
public ImmutableNormalObject getObj() {
return objFacade;
}
}
public class NormalObject {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ImmutableNormalObject {
private NormalObject obj;
public ImmutableNormalObject(Normalobject o) {
this.obj = o;
}
public String getName() {
return obj.getName();
}
}
Alternately, if it's acceptable to copy the object and it has a copy constructor (or you can add one), you could do that, but copy-and-return is expensive.
You can do this by returning a copy of your normalObject in getter:
public NormalObject getObj() {
return new NormalObject(obj.getName());
// or you can make a copy constructor:
// return new NormalObject(obj);
}
Or you can make a wrapper for your NormalObject that ignores name setter, but it brakes logic.
Please change Your NormalObject code to
public final class ImmutableObject {
private final String name;
// initialise it to null
private final NormalObject obj = null;
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
// use the Constructor for setting name only once during initialization of ImmutableObject via its constructor
obj = new NormalObject(name);
//obj.setName(name);
}
public NormalObject getObj() {
NormalObject tempObj = obj;
return tempObj;
}
}
NormalObject Class
public class NormalObject {
private String name;
public NormalObject(name){
this.name = name;
}
public String getName() {
return name;
}
//Remove any setter on NormalObject
/*public void setName(String name) {
this.name = name;
}*/
}
In an immutable object, if a User tries to change the state of the Object. either you won't allow or return a new Instance of the Immutable class.
So, since Date is a mutable class.
You can create an immutable wrapper around date, and you can expose only those methods, that are subject to be used in your Immutable-Date's perspective, but you return a new instance of your Immutable class, with the changed attribute of your new Date.
I don't think final would be required for Immutable variable, because it is already private and Immutable.
Example :
public class Main{
private ImmutableDate immutableDate;
public Main() {
this.immutableDate = new ImmutableDate(new Date());
}
public Main(Date date){
this.immutableDate = new ImmutableDate(date);
}
public ImmutableDate getDate() {
return immutableDate;
}
public class ImmutableDate{
// private constructor, so this can only be instantiated within the outer class
// therefore, final keyword not required for Date, as no access given to the variable
private Date date;
private ImmutableDate(Date date) {
this.date = date;
}
// Example methods from Date, that are required for our Immutable implementation
public Main setTime(long time){
Date date1 = new Date();
date1.setTime(time);
return new Main(date1);
}
#Override
public String toString() {
return date.toString();
}
}
}

Using reflection to set an object property

I getting class by name and i need to update them with respective data and my question is how to do it with java
I want to add the method some dummy data .
I don't know the class type I just getting the class name and use reflection to get his data
I use this code to get the class instance and
Class<?> classHandle = Class.forName(className);
Object myObject = classHandle.newInstance();
// iterate through all the methods declared by the class
for (Method method : classHandle.getMethods()) {
// find all the set methods
if (method.getName().matches("set[A-Z].*")
And know that I find the list of the set method I want to update it with data
how can I do that .
assume that In class name I got person and the class have setSalary and setFirstName etc
how can I set them with reflection ?
public class Person {
public void setSalery(double salery) {
this.salery = salery;
}
public void setFirstName(String FirstName) {
this.FirstName = FirstName;
}
}
Instead of trying to call a setter, you could also just directly set the value to the property using reflection. For example:
public static boolean set(Object object, String fieldName, Object fieldValue) {
Class<?> clazz = object.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, fieldValue);
return true;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
return false;
}
Call:
Class<?> clazz = Class.forName(className);
Object instance = clazz.newInstance();
set(instance, "salary", 15);
set(instance, "firstname", "John");
FYI, here is the equivalent generic getter:
#SuppressWarnings("unchecked")
public static <V> V get(Object object, String fieldName) {
Class<?> clazz = object.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return (V) field.get(object);
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
return null;
}
Call:
Class<?> clazz = Class.forName(className);
Object instance = clazz.newInstance();
int salary = get(instance, "salary");
String firstname = get(instance, "firstname");
To update the first name
First find the field you want to update
Then find the mutator (which accepts an argument of the field's type)
Finally execute the mutator on the object with the new value:
Field field=classHandle.getDeclaredField("firstName");
Method setter=classHandle.getMethod("setFirstName", field.getType());
setter.invoke(myObject, "new value for first name");
if (method.getName().matches("set[A-Z].*") {
method.invoke(person,salary)
// and so on
}
to know the parameters you can issue method.getPagetParameterTypes()
based on the result construct your parameters and supply.
package apple;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
/*
* Employe Details class
*/
class Employee {
private long id;
private String name;
private String userName;
private Address address;
private Contact contact;
private double salary;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
/*
* Address class for employee
*/
class Address {
private String city;
private String state;
private String country;
private int pincode;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public int getPincode() {
return pincode;
}
public void setPincode(int pincode) {
this.pincode = pincode;
}
}
/*
* Contact class for Employee
*/
class Contact {
private String email;
private String contactNo;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getContactNo() {
return contactNo;
}
public void setContactNo(String contactNo) {
this.contactNo = contactNo;
}
}
public class Server {
public static void main(String args[]) throws JsonSyntaxException, Exception{
Gson gson = new Gson();
/*
* Old Employee Data
*/
Address address = new Address();
Contact contact = new Contact();
Employee employee = new Employee();
address.setCity("shohna-road");
address.setCountry("INDIA");
address.setPincode(12201);
address.setState("Hariyana");
contact.setContactNo("+918010327919");
contact.setEmail("shivritesh9984#gmail.com");
employee.setAddress(address);
employee.setContact(contact);
employee.setId(4389573);
employee.setName("RITESH SINGH");
employee.setSalary(43578349.345);
employee.setUserName("ritesh9984");
System.out.println("Employee : "+gson.toJson(employee));
/* New employee data */
Employee emp = employee;
address.setCity("OMAX");
emp.setAddress(address);
emp.setName("RAVAN");
/* Update employee with new employee Object*/
update(employee, gson.fromJson(gson.toJson(emp), JsonObject.class) );
System.out.println("Employee-Update : "+gson.toJson(employee));
}
/*
* This method update the #target with new given value of new object in json object form
*/
public static void update(Object target, JsonObject json) throws Exception {
Gson gson=new Gson();
Class<? > class1 = target.getClass();
Set<Entry<String, JsonElement>> entrySet = json.entrySet();
for (Entry<String, JsonElement> entry : entrySet) {
String key = entry.getKey();
Field field = class1.getDeclaredField(key);
field.setAccessible(true);
Type genType = field.getGenericType();
field.set(target,
gson.fromJson(entry.getValue(),genType));
}
}
}

Categories

Resources