I need to create a JSON object structure from set of java pojo classes. This will provide a better understand of how the objects are structured by just looking at the Json file. I tried both Gson and org.codehaus.jackson.map.ObjectMapper libraries. But couldn't generate all the Json tags relevant to all the fields in java pojo objects. The created json file is only having the values from the values stetted objects. I need to have all the fields of pojo objects in the Json file.
Gson
Hotel hotel = new Hotel(); //This hotel object includes multiple objects and those objects also include multiple objects, lists
Gson gson = new GsonBuilder().create();
String json = gson.toJson(hotel);
System.out.println(json);
ObjectMapper
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(new File("/home/Pojos.json");
This is the class and json response I got.
Hotel.java
package datatypes;
import java.util.ArrayList;
import java.util.List;
public class Hotel {
private String hotelCode;
private String chainCode;
private String hotelName;
private List<Room> rooms = new ArrayList<Room>();
private List<RoomRateRestriction> roomRateRestrictions
= new ArrayList<RoomRateRestriction>();
public String getHotelCode() {
return hotelCode;
}
public void setHotelCode(String hotelCode) {
this.hotelCode = hotelCode;
}
public String getChainCode() {
return chainCode;
}
public void setChainCode(String chainCode) {
this.chainCode = chainCode;
}
public String getHotelName() {
return hotelName;
}
public void setHotelName(String hotelName) {
this.hotelName = hotelName;
}
public List<Room> getRooms() {
return rooms;
}
public void setRooms(List<Room> rooms) {
this.rooms = rooms;
}
public void addRoom(Room room){
this.rooms.add(room);
}
public List<RoomRateRestriction> getRoomRateRestrictions() {
return roomRateRestrictions;
}
public void setRoomRateRestrictions(
List<RoomRateRestriction> roomRateRestrictions) {
this.roomRateRestrictions = roomRateRestrictions;
}
public void addRoomRateRestrictions(
RoomRateRestriction roomRateRestriction) {
this.roomRateRestrictions.add(roomRateRestriction);
}
}
JSON
{
"rooms":[],
"roomRateRestrictions":[]
}
Add serializeNulls() on GsonBuilder() to serialize null fields
Configure Gson to serialize null fields. By default, Gson omits all fields that are null
* during serialization.
class Hotel {
private String name;
private List<Guest> guests = new ArrayList<>(Collections.singleton(new Guest())); //you have to set a value in order to get the structure of the Guest class otherwise it will show an empty list []
//getter setter
}
class Guest {
private String name;
//getter setter
}
Gson
Hotel hotel = new Hotel();
Gson gson = new GsonBuilder().serializeNulls().create();
String json = gson.toJson(hotel);
System.out.println(json);
Output
{
"name":null,
"guests":[
{
"name":null
}
]
}
You can use jackson-databind library.
Maven dependency is :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10</version>
</dependency>
Also please find some of sample example below. Here I used Employee Object which is having nested Address object inside it.
Employee.java
public class Employee {
private int empId;
private String firstName;
private String lastName;
private double salary;
private Address address;
public Employee() {}
public Employee(int empId, String firstName, String lastName, double salary) {
this.empId = empId;
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
}
public Employee(int empId, String firstName, String lastName, double salary, Address address) {
this.empId = empId;
this.firstName = firstName;
this.lastName = lastName;
this.salary = salary;
this.address = address;
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address.java
public class Address {
private String street;
private String city;
private String state;
private String zip;
private String country;
public Address() {}
public Address(String street, String city, String state, String zip, String country) {
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
this.country = country;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
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 getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
I converted a Sample java pojo class to Json and also JSON to pojo object back.
JacksonConvertion.java
public class JacksonConvertion {
public static void main(String[] args) {
pojoToJson();
jsonToPojo();
}
private static void pojoToJson() {
try {
System.out.println("Convert Object to json ");
ObjectMapper mapper = new ObjectMapper();
Address address = new Address("#103, 1st cross, manyta tech park", "Bengaluru", "Karnataka", "560010",
"India");
Address address1 = new Address();
address1.setCity("Bengaluru");
address1.setState("Karnataka");
address1.setCountry("India");
Employee emp = new Employee(1233, "Raju", "BG", 98734.23, address1);
mapper.writeValue(new File("staff.json"), emp);
String jsonString = mapper.writeValueAsString(emp);
System.out.println(jsonString);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void jsonToPojo() {
try {
System.out.println("Convert Json to Object ");
ObjectMapper mapper = new ObjectMapper();
Employee employee = mapper.readValue(new File("staff.json"), Employee.class);
System.out.println(employee.getFirstName());
System.out.println(employee.getSalary());
System.out.println(employee.getAddress().getCity());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Also included some of links for your reference.
Link1
Link2
I think this would work fine
Add http://www.java2s.com/Code/Jar/c/Downloadcomfasterxmljacksondatabindjar.htm and http://www.java2s.com/Code/Jar/j/Downloadjacksondatabind205sourcesjar.htm to your library.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class javaPojo {
public static void main(String args[]){
Hotel hotel = new Hotel();
ObjectMapper mapper = new ObjectMapper();
try {
String json = mapper.writeValueAsString(hotel);
System.out.println("JSON = " + json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}}
}
Hotel
import java.util.ArrayList;
import java.util.List;
public class Hotel {
private String hotelCode;
private String chainCode;
private String hotelName;
private List<Room> rooms = new ArrayList<Room>();
private List<RoomRateRestriction> roomRateRestrictions
= new ArrayList<RoomRateRestriction>();
public String getHotelCode() {
return hotelCode;
}
public void setHotelCode(String hotelCode) {
this.hotelCode = hotelCode;
}
public String getChainCode() {
return chainCode;
}
public void setChainCode(String chainCode) {
this.chainCode = chainCode;
}
public String getHotelName() {
return hotelName;
}
public void setHotelName(String hotelName) {
this.hotelName = hotelName;
}
public List<Room> getRooms() {
return rooms;
}
public void setRooms(List<Room> rooms) {
this.rooms = rooms;
}
public void addRoom(Room room){
this.rooms.add(room);
}
public List<RoomRateRestriction> getRoomRateRestrictions() {
return roomRateRestrictions;
}
public void setRoomRateRestrictions(
List<RoomRateRestriction> roomRateRestrictions) {
this.roomRateRestrictions = roomRateRestrictions;
}
public void addRoomRateRestrictions(
RoomRateRestriction roomRateRestriction) {
this.roomRateRestrictions.add(roomRateRestriction);
}
}
Room
public class Room {
}
RoomRateRestriction
public class RoomRateRestriction {
}
OUTPUT - JSON = {"hotelCode":null,"chainCode":null,"hotelName":null,"rooms":[],"roomRateRestrictions":[]}
Related
I am trying to merge JSON objects with the new #JsonMerge annotation. I found a sample online that works when I run it in my IDE. Here's a snippet to run:
#Test
void mergeTest() throws IOException {
final Employee employee = new Employee("Serializon", new Address("Street 1", "City 1", "ZipCode1"));
final Employee newEmployee = new Employee("Serializon", new Address("Street 2", "City 2", "ZipCode2"));
assertThat(employee.getAddress().getCity()).isEqualTo("City 1");
final ObjectMapper objectMapper = new ObjectMapper();
final Employee mergedEmployee = objectMapper.readerForUpdating(employee).readValue(JSONUtil.toJSON(newEmployee));
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedEmployee));
assertThat(newEmployee.getAddress().getCity()).isEqualTo("City 2");
assertThat(mergedEmployee.getAddress().getCity()).isEqualTo("City 2");
}
public class Employee {
private String name;
#JsonMerge
private Address address;
public Employee(final String name, final Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
public class Address {
private String street;
private String city;
private String zipCode;
public Address(final String street, final String city, final String zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZipCode() {
return zipCode;
}
}
When I try to reproduce this with my own class, it fails with the following error:
Deserialization of [simple type, class package.State] by passing existing instance (of package.State) not supported
My class in question is a POJO with some lists and primitive properties, all with getters. It is constructed using a builder and is immutable. It looks like this:
#JsonDeserialize(builder = State.Builder.class)
public class State {
private final String id;
#JsonMerge
private final List<Module> modules;
protected State(final Builder builder) {
this.id = builder.id;
this.modules = builder.modules;
}
public static Builder builder() {
return new Builder();
}
public String getId() {
return id;
}
public List<Module> getModules() {
return modules;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static final class Builder {
private String id;
private List<Module> modules;
private Builder() {
}
public Builder withId(final String id) {
this.id = id;
return this;
}
public Builder withModules(final List<Module> modules) {
this.modules = modules;
return this;
}
public State build() {
return new State(this);
}
}
}
The merge annotation states the following:
Merging is only option if there is a way to introspect current state: if there is accessor (getter, field) to use. Merging can not be enabled if no accessor exists or if assignment occurs using a Creator setter (constructor or factory method), since there is no instance with state to introspect.
So I thought perhaps the builder might the problem, but retrofitting the Employee/Address sample with a builder still works:
#Test
void mergeTest() throws IOException {
final Employee employee = Employee.newBuilder()
.withName("Serializon")
.withAddress(Address.newBuilder()
.withStreet("Steet 1")
.withCity("City 1")
.withZipCode("ZipCode1")
.build())
.build();
assertThat(employee.getAddress().getCity()).isEqualTo("City 1");
final Employee newEmployee = Employee.newBuilder()
.withName("Serializon")
.withAddress(Address.newBuilder()
.withStreet("Steet 2")
.withCity("City 2")
.withZipCode("ZipCode2")
.build())
.build();
final ObjectMapper objectMapper = new ObjectMapper();
final Employee mergedEmployee = objectMapper.readerForUpdating(employee).readValue(JSONUtil.toJSON(newEmployee));
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedEmployee));
assertThat(newEmployee.getAddress().getCity()).isEqualTo("City 2");
assertThat(mergedEmployee.getAddress().getCity()).isEqualTo("City 2");
}
#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
private String name;
#JsonMerge
private Address address;
private Employee(final Builder builder) {
name = builder.name;
address = builder.address;
}
public static Builder newBuilder() {
return new Builder();
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
public static final class Builder {
private String name;
private Address address;
private Builder() {
}
public Builder withName(final String name) {
this.name = name;
return this;
}
public Builder withAddress(final Address address) {
this.address = address;
return this;
}
public Employee build() {
return new Employee(this);
}
}
}
#JsonDeserialize(builder = Address.Builder.class)
public class Address {
private String street;
private String city;
private String zipCode;
private Address(final Builder builder) {
street = builder.street;
city = builder.city;
zipCode = builder.zipCode;
}
public static Builder newBuilder() {
return new Builder();
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZipCode() {
return zipCode;
}
public static final class Builder {
private String street;
private String city;
private String zipCode;
private Builder() {
}
public Builder withStreet(final String street) {
this.street = street;
return this;
}
public Builder withCity(final String city) {
this.city = city;
return this;
}
public Builder withZipCode(final String zipCode) {
this.zipCode = zipCode;
return this;
}
public Address build() {
return new Address(this);
}
}
}
Finally I tried to have a list of addresses instead, and accepting the list in the builder as withAddresses instead. So, for brevity:
#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
#JsonMerge
private List<Address> addresses;
public static final class Builder {
public Builder withAddresses(final List<Address> addresses) {
this.addresses = addresses;
return this;
}
}
}
And when I run the testcase again, this fails with the same error as my own code:
Deserialization of [simple type, class se.itab.locker.core.util.Employee] by passing existing instance (of se.itab.locker.core.util.Employee) not supported
What is actually going on here, and can I resolve it somehow or is this an unsupported use case or bug?
Update
So I found that this works:
//#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
#JsonCreator
public Employee(final Employee employee) {
name = employee.name;
addresses = employee.addresses;
stringAddresses = employee.stringAddresses;
}
But then serializing causes an infinite loop instead.
This api works for inserting and updating but everytim I update the image dissapears so I am trying to say orElse if image not exist set image to existing image any advice pls
#RequestMapping(value = "/updateBank", method = RequestMethod.POST, consumes = "multipart/form-data")
public ResponseEntity<Bank> updateBank(
#RequestPart("bank") #Valid Bank bank,
#RequestPart("file") #Valid Optional<MultipartFile> image) throws IOException {
// routine to update a bank including image
image.ifPresent(pic ->
{
try {
bank.setImage(new Binary(BsonBinarySubType.BINARY, pic.getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
});
Bank result = bankRepository.save(bank);
return ResponseEntity.ok().body(result);
}
#Document(collection = "mst_Bank")
public class Bank {
#Id
private String id;
private String name;
private String address;
private String country;
private String region;
private String city;
private String swiftCode;
private String routeCode;
private Binary flag;
private Binary image;
public Bank(String name, String address, String country, String region, String city, String swiftCode,
String routeCode) {
this.name = name;
this.address = address;
this.country = country;
this.region = region;
this.city = city;
this.swiftCode = swiftCode;
this.routeCode = routeCode;
}
public Bank() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getSwiftCode() {
return swiftCode;
}
public void setSwiftCode(String swiftCode) {
this.swiftCode = swiftCode;
}
public String getRouteCode() {
return routeCode;
}
public void setRouteCode(String routeCode) {
this.routeCode = routeCode;
}
public Binary getImage() {
return image;
}
public void setImage(Binary image) {
this.image = image;
}
}
/No Point of this
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details./
Use Something like this
#RequestMapping(value = "/updateEntity", method = RequestMethod.POST, consumes = "multipart/form-data")
public ResponseEntity<Entity> updateEntity(#RequestPart("entity") #Valid Entity entity, #RequestPart("file") #Valid Optional<MultipartFile> image) throws IOException {
// routine to update a entity including image
byte[] imageData = null;
if (image.isPresent() && image.get() != null)
imageData = image.get().getBytes();
if (imageData == null && entity.getId() != null) {
Optional<Entity> readEntity = entityRepository.findById(entity.getId());
if (readEntity.get() != null)
imageData = readEntity.get().getImage().getData();
}
if (imageData != null) {
entity.setImage(new Binary(BsonBinarySubType.BINARY, imageData));
}
Entity result = entityRepository.save(entity);
return ResponseEntity.ok().body(result);
}
I am implementing a sample Spring MVC Form with Form Validation. I have a complex type Address as bean property for Student form bean. And I have added form validation #NotEmpty for Address bean properties. But the same is not reflecting in the UI. But form validation works for other primitive types of Student form bean.
So, Validation works perfectly for Student form bean but not for nested complex types like Address within Student form bean.
I am trying understand the reason and a fix.
Spring version 4.0+.
Hibernate Validator api:5.2.4
Student POJO:
package com.xyz.form.beans;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
import com.xyz.validators.DateNotEmpty;
import com.xyz.validators.ListNotEmpty;
public class Student {
#Size(min = 2, max = 30)
private String firstName;
#Size(min = 2, max = 30)
private String lastName;
#NotEmpty
private String gender;
#DateNotEmpty
#Past
private Date DOB;
private String email;
private String mobileNumber;
#ListNotEmpty
private List<String> courses;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Date getDOB() {
return DOB;
}
public void setDOB(Date dOB) {
DOB = dOB;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public List<String> getCourses() {
return courses;
}
public void setCourses(List<String> courses) {
this.courses = courses;
}
}
Address POJO:
package com.xyz.form.beans;
import org.hibernate.validator.constraints.NotEmpty;
import com.xyz.validators.LongNotEmpty;
public class Address {
#NotEmpty
private String houseNo;
#NotEmpty
private String street;
#NotEmpty
private String area;
#NotEmpty
private String city;
#LongNotEmpty
private Long pin;
public String getHouseNo() {
return houseNo;
}
public void setHouseNo(String houseNo) {
this.houseNo = houseNo;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Long getPin() {
return pin;
}
public void setPin(Long pin) {
this.pin = pin;
}
}
Student Controller:
#RequestMapping(value = "/newStudentDetails.do", method = RequestMethod.POST)
public ModelAndView newStudentDetails(
#Valid #ModelAttribute("student") com.xyz.form.beans.Student studentFormBean,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return new ModelAndView("newStudentPage");
}
Student studentDto = new Student();
studentDto.setFirstName(studentFormBean.getFirstName());
studentDto.setLastName(studentFormBean.getLastName());
studentDto.setGender(studentFormBean.getGender());
studentDto.setDOB(new Date(studentFormBean.getDOB().getTime()));
studentDto.setEmail(studentFormBean.getEmail());
studentDto.setMobileNumber(studentFormBean.getMobileNumber());
StringBuilder sb = new StringBuilder();
sb.append(studentFormBean.getAddress().getHouseNo() + ", ");
sb.append(studentFormBean.getAddress().getStreet() + ", ");
sb.append(studentFormBean.getAddress().getArea() + ", ");
sb.append(studentFormBean.getAddress().getCity() + "-");
sb.append(studentFormBean.getAddress().getPin());
studentDto.setAddress(sb.toString());
studentDto.setCourses(studentFormBean.getCourses());
studentDao.createStudent(studentDto);
ModelAndView mav = new ModelAndView("newStudentSuccess");
return mav;
}
Thanks,
Viswanath
You need to annotate your complex types with #Valid.
This is the reference (which references here)
Hi lets try #ModelAttribute("student") #Valid com.xyz.form.beans.Student studentFormBean in place of #Valid #ModelAttribute("student")
For nested complex types, you have to activate the direct field access. Just like below:
#org.springframework.web.bind.annotation.ControllerAdvice
public class ControllerAdvice {
#InitBinder
public void initBinder(WebDataBinder webDataBinder) {
webDataBinder.initDirectFieldAccess();
}
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));
}
}
}
I am playing with the Jackson examples and am having some trouble getting deserialization to work with immutable classes and interfaces.
Below is my code:
package com.art.starter.jackson_starter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
/** * Hello world! * */ public class App {
public static void main( String[] args ) throws JsonGenerationException, JsonMappingException, IOException
{
System.out.println( "Hello World!" );
AddressImpl.AddressBuilder builder = new AddressImpl.AddressBuilder();
NameImpl.Builder nameBuilder = new NameImpl.Builder();
UserImpl.Builder userBuilder = new UserImpl.Builder();
Name name = nameBuilder.first("FirstName")
.last("LastName")
.build();
Address address = builder.setCity("TestCity")
.setCountry("TestCountry")
.setState("PA")
.setStreet("TestAddress")
.setZip(123)
.build();
User user = userBuilder.address(address)
.gender(User.Gender.MALE)
.isVerified(true)
.userImage(new byte[5])
.build();
System.out.println(address);
System.out.println(name);
System.out.println(user);
StringWriter sw = new StringWriter();
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(sw, user);
System.out.println(sw);
StringReader sr = new StringReader("{\"address\":{\"state\":\"PA\",\"country\":\"TestCountry\",\"street\":\"TestAddress\",\"city\":\"TestCity\",\"zip\":123},\"verified\":true,\"gender\":\"MALE\",\"userImage\":\"AAAAAAA=\"}");
/*
This line throws the Exception
*/
User user2 = mapper.readValue(sr, UserImpl.class);
System.out.println(user2);
} }
package com.art.starter.jackson_starter;
import java.util.Arrays;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
public final class UserImpl implements User
{
private final Address address;
private final Gender gender;
private final byte[] userImage;
private final boolean isVerified;
public static class Builder
{
private Address address;
private Gender gender;
// private Name name;
private byte[] userImage;
private boolean isVerified;
public Builder address(Address address)
{
this.address = address;
return this;
}
public Builder gender(Gender gender)
{
this.gender = gender;
return this;
}
// public Builder name(Name name)
// {
// this.name = name;
// return this;
// }
public Builder userImage(byte[] userImage)
{
this.userImage = userImage;
return this;
}
public Builder isVerified(boolean isVerified)
{
this.isVerified = isVerified;
return this;
}
public UserImpl build()
{
return new UserImpl(address, gender, userImage, isVerified);
}
}
#JsonCreator
public UserImpl(#JsonProperty("address") Address address, #JsonProperty("gender") Gender gender, #JsonProperty("userImage") byte[] userImage,
#JsonProperty("verified") boolean isVerified)
{
super();
this.address = address;
this.gender = gender;
this.userImage = userImage;
this.isVerified = isVerified;
}
public Address getAddress()
{
return address;
}
public Gender getGender()
{
return gender;
}
public byte[] getUserImage()
{
return userImage;
}
public boolean isVerified()
{
return isVerified;
}
#Override
public String toString()
{
StringBuilder builder2 = new StringBuilder();
builder2.append("UserImpl [address=");
builder2.append(address);
builder2.append(", gender=");
builder2.append(gender);
builder2.append(", isVerified=");
builder2.append(isVerified);
builder2.append(", name=");
builder2.append(", userImage=");
builder2.append(Arrays.toString(userImage));
builder2.append("]");
return builder2.toString();
}
}
package com.art.starter.jackson_starter;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
public final class AddressImpl implements Address
{
private final String city;
private final String country;
private final String street;
private final String state;
private final int zip;
public static class AddressBuilder
{
private String city;
private String country;
private String street;
private String state;
private int zip;
public AddressBuilder setCity(String city)
{
this.city = city;
return this;
}
public AddressBuilder setCountry(String country)
{
this.country = country;
return this;
}
public AddressBuilder setStreet(String street)
{
this.street = street;
return this;
}
public AddressBuilder setState(String state)
{
this.state = state;
return this;
}
public AddressBuilder setZip(int zip)
{
this.zip = zip;
return this;
}
public AddressImpl build()
{
return new AddressImpl(city, country, street, state, zip);
}
}
#JsonCreator
public AddressImpl(#JsonProperty("city") String city, #JsonProperty("country") String country, #JsonProperty("street") String street,
#JsonProperty("state") String state, #JsonProperty("zip") int zip)
{
this.city = city;
this.country = country;
this.street = street;
this.state = state;
this.zip = zip;
}
public String getCity()
{
return city;
}
public String getCountry()
{
return country;
}
public String getStreet()
{
return street;
}
public String getState()
{
return state;
}
public int getZip()
{
return zip;
}
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("AddressImpl [city=");
builder.append(city);
builder.append(", country=");
builder.append(country);
builder.append(", state=");
builder.append(state);
builder.append(", street=");
builder.append(street);
builder.append(", zip=");
builder.append(zip);
builder.append("]");
return builder.toString();
}
}
The issue appears to be with Address. I get this exception:
Exception in thread "main" org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.art.starter.jackson_starter.Address, problem: abstract types can only be instantiated with additional type information
at [Source: java.io.StringReader#785f8172; line: 1, column: 2]
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
at org.codehaus.jackson.map.deser.StdDeserializationContext.instantiationException(StdDeserializationContext.java:212)
at org.codehaus.jackson.map.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:97)
at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:230)
at org.codehaus.jackson.map.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:595)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:472)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:350)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2391)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1614)
at com.art.starter.jackson_starter.App.main(App.java:56)
I am sure this is because there is no way for Jackson to resolve Address which is an interface to AddressImpl which is a concrete implementation. I have been poking through the docs and have looked at a few articles regarding the #JsonDeserialize(as=AddressImpl.class),but it didn't work. So I am stumped. Has anyone ever gotten this to work, is it even supported?
It works like a champ if I replace Address with AddressImpl in the UserImpl class.
Just in case you hadn't seen it, here's a blog entry that discusses working with immutable objects and Jackson.
But you should definitely be able to use #JsonDeserialize(as=AddressImpl.class); either by adding it to Address.java interface (either directly or by using mix-ins), or by adding it to field or property. One thing to note is that for deserialization, it MUST be next to accessor you use; setter if you have one, if not, next to field. Annotations are not (yet) shared between accessors; so for example adding it to 'getter' would not work.
Jackson 1.8 also finally allows registration of abstract-to-concrete types (see http://jira.codehaus.org/browse/JACKSON-464 for more details) which might be the best option to indicate that 'AddressImpl' is to be used for 'Address'.