There is a class with another static one inside. Jackson calls setters while deserializing json. But some setters doesn't work as expected.
Classes def:
#Entity
#JsonIgnoreProperties(ignoreUnknown=true)
#JsonPropertyOrder({ "name", "imageLink", "weeklyGross", "weeklyChange" })
public class WeeklyBoxOffice {
#Embeddable
public static class WeeklyBoxOfficeID implements Serializable {
public String country;
public String name;
public WeeklyBoxOfficeID() {
}
}
public WeeklyBoxOffice() {
this.id = new WeeklyBoxOfficeID();
}
#EmbeddedId
#JsonUnwrapped
WeeklyBoxOfficeID id;
private long weeklyGross;
private double weeklyChange;
// "weeklyGross":"$294,710"
public void setWeeklyGross(String weeklyGross) throws ParseException {
System.out.println("setWeeklyGross called, weeklyGross = " + weeklyGross);
this.weeklyGross = (long) NumberFormat.getCurrencyInstance(Locale.US)
.parse(weeklyGross);
}
// "weeklyChange": "-57.1%"
public void setWeeklyChange(String weeklyChange) {
this.weeklyChange = weeklyChange.equals("-") ? 0 : Double.parseDouble(weeklyChange.replace('%', ' '));
}
public void setCountry(String country) {
this.id.country = country;
}
// #JsonProperty("name")
public void setName(String name) {
System.out.println("Set name caled, name = " + name);
this.id.name = name;
}
public void setMovie(Movie movie) {
this.movie = movie;
}
public static void main(String[] args) throws IOException {
String json = "{\n" +
" \"TrendingMovies\": [\n" +
" {\n" +
" \"name\": \"The Dark Tower\",\n" +
" \"weeklyGross\": \"$461,377\",\n" +
" \"weeklyChange\": \"5.0\"\n" +
" }\n" +
" ]\n" +
"}\n";
ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
List<WeeklyBoxOffice> list = mapper.readValue(mapper.readTree(json).get("TrendingMovies").toString(), new TypeReference<List<WeeklyBoxOffice>>() {});
list.forEach(System.out::println);
}
Output:
Set name caled, name = The Dark Tower
setWeeklyGross called, weeklyGross = $461,377
WeeklyBoxOffice{ id=WeeklyBoxOfficeID{country='null', name='null'},
weeklyGross=461377, weeklyChange=5.0 }
Why call of setName has no effect while setWeeklyGross and setWeeklyChange does and the fields of nested object remains null?
The issue is the #JsonUnwrapped and it will work fine if your remove it. If you follow your code with a debugger, you'll see that Jackson (in this order):
Calls WeeklyBoxOffice constructor that sets WeeklyBoxOfficeID id to an empty object (all null).
Calls setters for each JSON field by matching names. setName is called and name is set inside WeeklyBoxOfficeID id.
Handles any fields annotated as unwrapped. It can't find a name field in JSON as it was consumed before by getName. Therefore it now sets it to null.
The code will also work if you keep #JsonUnwrapped but remove the setCountry and setName setters which is presumably simpler. Basically use one or the other.
Related
Request:
{
"name":"iswarya",
"dept":{
"deptName":"eee",
"location":"firstfloor"
},
"additionalDetails":{
"projectName":"finalyearproject"
}
}
Response:
{
"name": "iswarya",
"deptName": null,
"location": null,
"projectName": null
}
Controller class:
#PostMapping(value="/objectMApper")
public String createEmployee(#RequestBody AnnotationTestBean demoEntity) throws JsonProcessingException {
ObjectMapper obj=new ObjectMapper();
return obj.writeValueAsString(demoEntity);
}
In the given example the request for JSON is not wrapped, so its dept and additionalDetails should not be annotated with #JsonUnwrapped.
Instead, a response should be created extending the request class, having a copy constructor, and overriding appropriate getters annotated as #JsonUnwrapped.
The example below uses Lombok annotations to generate getters/setters/constructors.
#Data
#AllArgsConstructor
#NoArgsConstructor
static class Request {
private String name;
private Department dept;
private Details additionalDetails;
}
#Data
static class Department {
private String deptName;
private String location;
}
#Data
static class Details {
private String projectName;
}
static class Response extends Request {
public Response(Request request) {
super(request.name, request.dept, request.additionalDetails);
}
#Override #JsonUnwrapped
public Department getDept() { return super.getDept(); }
#Override #JsonUnwrapped
public Details getAdditionalDetails() { return super.getAdditionalDetails(); }
}
Test
ObjectMapper om = new ObjectMapper();
String json = "{\r\n" +
" \"name\":\"iswarya\",\r\n" +
" \"dept\":{\r\n" +
" \"deptName\":\"eee\",\r\n" +
" \"location\":\"firstfloor\"\r\n" +
" },\r\n" +
" \"additionalDetails\":{\r\n" +
" \"projectName\":\"finalyearproject\"\r\n" +
" }\r\n" +
"}";
Request request = om.readValue(json, Request.class);
Response response = new Response(request);
String str = om.writerWithDefaultPrettyPrinter().writeValueAsString(response);
System.out.println(str);
Output
{
"name" : "iswarya",
"deptName" : "eee",
"location" : "firstfloor",
"projectName" : "finalyearproject"
}
I have access to a RESTful API which returns JSON Strings, such as the following:
{
"Container1": {
"active": true
},
"Container2": {
"active": false
},
}
The problem is that the RESTful API is a bit maldesigned. The field name contains the data already. With the Jackson library it is not possible to deserialize the field name to a property name of the corresponding Java bean class. I assume, this isn't intended by the JSON specification neither. The above JSON string needs to be deserialized to an instance of the following class:
public class Container {
private Boolean active;
private String name;
}
I end up with UnrecognizedPropertyException for the field Container1.
I thought to configure to ignore unknown properties and to provide a JsonDeserializer for that property like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Container {
private Boolean active;
private String name;
#JsonDeserialize(using = FieldNameToPropertyDeserializer.class)
public void setName(String name) {
this.name = name;
}
}
and the FieldNameToPropertyDeserializer:
public class FieldNameToPropertyDeserializer extends StdDeserializer<String> {
public FieldNameToPropertyDeserializer() {
super(String.class);
}
#Override
public String deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
return parser.getCurrentName();
}
}
The invocation of the deserialization is achieved as follows:
String jsonString = response.readEntity(String.class);
ObjectMapper objectMapper = new ObjectMapper();
ObjectReader readerFor = objectMapper.readerFor(Container.class);
MappingIterator<Container> mappingIterator = readerFor.readValues(jsonString);
while (mappingIterator.hasNext()) {
Container container = (Container) mappingIterator.next();
containers.add(container);
}
But I only receive empty objects (properties set to null) because the parsing of the properties is skipped since I set #JsonIgnoreProperties(ignoreUnknown = true).
Is this possible at all? Or should I implement something like a post-processing afterwards?
How about this. Create a class ContainerActive like this
public class ContainerActive {
private boolean active;
// constructors, setters, getters
}
And you could just do
Map<String, ContainerActive> map = mapper.readValue(jsonString, new TypeReference<Map<String, ContainerActive>>() {});
With this you will have "Container1", "Container2" as the keys and ContainerActive Object as values which has active field.
Just a quick solution, if the object is such that, that all of it object is a container object you can receive the JSON inside and JSONObject you may use below code
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestSO {
public static void main(String[] args) throws JsonParseException, JsonMappingException, JSONException, IOException {
String jsonString = "{\r\n" +
" \"Container1\": {\r\n" +
" \"active\": true\r\n" +
" },\r\n" +
" \"Container2\": {\r\n" +
" \"active\": false\r\n" +
" },\r\n" +
"}";
JSONObject jsonObject = new JSONObject(jsonString);
ObjectMapper mapper = new ObjectMapper();
for (String key : jsonObject.keySet()) {
Container container = mapper.readValue(jsonObject.get(key).toString(), Container.class);
System.out.println(container);
}
}
static class Container{
private String name;
private Boolean active;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
#Override
public String toString() {
return "Container [name=" + name + ", active=" + active + "]";
}
}
}
I am working with REST APIs , Java, Hibernate and JPA.
I have a class Mstrgetnroletype (i.e. role type) that is part of another class called ApplicationUser.
class ApplicationUser
#Entity
#Table(name = "APPLICATIONUSER", schema = "orcl", uniqueConstraints = { #UniqueConstraint(columnNames = "EMAIL"),
#UniqueConstraint(columnNames = "USERNAME") })
public class ApplicationUser implements java.io.Serializable {
private BigDecimal id;
private Mstrgenroletype mstrgenroletype;
private Mstrgenyesno mstrgenyesnoByIsactive;
private Mstrgenyesno mstrgenyesnoByIsstaff;
private Mstrgenyesno mstrgenyesnoByIssuperuser;
[.. snip ..]
class Mstrgenroletype
#Entity
#Table(name = "MSTRGENROLETYPE", schema = "orcl")
public class Mstrgenroletype implements java.io.Serializable {
private BigDecimal mstrgenroletypeid;
private String langid;
private String shortdesc;
private String longdesc;
private Set<Applicationuser> applicationusers = new HashSet<Applicationuser>(0);
public Mstrgenroletype() {
}
public Mstrgenroletype(BigDecimal mstrgenroletypeid) {
this.mstrgenroletypeid = mstrgenroletypeid;
}
public Mstrgenroletype(BigDecimal mstrgenroletypeid, String langid, String shortdesc, String longdesc,
Set<Applicationuser> applicationusers) {
this.mstrgenroletypeid = mstrgenroletypeid;
this.langid = langid;
this.shortdesc = shortdesc;
this.longdesc = longdesc;
this.applicationusers = applicationusers;
}
#Id
#Column(name = "MSTRGENROLETYPEID", unique = true, nullable = false, precision = 22, scale = 0)
public BigDecimal getMstrgenroletypeid() {
return this.mstrgenroletypeid;
}
public void setMstrgenroletypeid(BigDecimal mstrgenroletypeid) {
this.mstrgenroletypeid = mstrgenroletypeid;
}
#Column(name = "LANGID", length = 2)
public String getLangid() {
return this.langid;
}
public void setLangid(String langid) {
this.langid = langid;
}
#Column(name = "SHORTDESC", length = 10)
public String getShortdesc() {
return this.shortdesc;
}
public void setShortdesc(String shortdesc) {
this.shortdesc = shortdesc;
}
#Column(name = "LONGDESC", length = 20)
public String getLongdesc() {
return this.longdesc;
}
public void setLongdesc(String longdesc) {
this.longdesc = longdesc;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "mstrgenroletype")
public Set<Applicationuser> getApplicationusers() {
return this.applicationusers;
}
public void setApplicationusers(Set<Applicationuser> applicationusers) {
this.applicationusers = applicationusers;
}
#Override
public String toString() {
return "Mstrgenroletype [mstrgenroletypeid=" + mstrgenroletypeid + ", langid=" + langid + ", shortdesc="
+ shortdesc + ", longdesc=" + longdesc + "]";
}
}
I am trying to make a method of "toString" that will print out the contents of the variable associated with ApplicationUser.
The problem is that when printing out the contents of the variable associated with Mstrgenroletype, I get an error of
org.hibernate.LazyInitializationException: could not initialize proxy
- no Session
This is because it is trying to print the contents associated with the class of Mstrgenroletype. (I know this because when I remove the print statement assocaited with variable that is linked to the class, everything works)
BEFORE (getting the error)
#Override
public String toString() {
return "ApplicationUser [id=" + id + ", password=" + password + ", username=" + ", role=" + mstrgenroletype.toString()
+ username + ", firstname=" + firstname + ", lastname=" + lastname + ", email=" + email + ", userid="
+ userid + ", apptenantid=" + apptenantid + ", appkeyid=" + appkeyid + ", profilepic=" + profilepic + "]";
}
AFTER (Mstrgenroltype variable removed, getting no error)
#Override
public String toString() {
return "ApplicationUser [id=" + id + ", password=" + password + ", username="
+ userid + ", apptenantid=" + apptenantid + ", appkeyid=" + appkeyid + ", profilepic=" + profilepic + "]";
}
I have made sure that the class with Mstrgenroletype does have a toString method as well.
How can I print the variable associated with Mstrgenroletype?
You invoke the toString() method on a ApplicationUser object that didn't load the mstrgenroletype relationship eagerly.
As ApplicationUser.toString() method invokes the toString() method of the object referenced by mstrgenroletype, it throws an exception because the ApplicationUser object is currently a Hibernate proxy that can load relationship only in the frame of a Hibernate session.
But according to the thrown exception, you don't have any currently opened session during the toString() invocation.
How can I print the variable associated with Mstrgenroletype?
First, you should avoid having a toString() which the results depends on a persistence session.
It mix things. toString() is for debugging an object, not for fetching fields with query executions.
It is an important side effect you have to avoid.
So I advise to write a toString() method that relies only on own fields of the entity as this one you wrote in your workaround.
This rule should also be followed for equals() or hashCode() methods
Of course if functionally you need to retrieve the relationship, add it in your query with a fetch join.
If it is not the case and you want only to debug the object relationships you can add a breakpoint in your IDE in a class where the Hibernate session is opened and the object retrieved and dig into the relationship with IDE tools (display or execute feature in Eclipse).
I have to deserialize following json using Jackson library into Customer class
{
"code":"C001",
"city": "Pune",
"street": "ABC Road"
}
and Classes as
class Address{
String city;
String street;
}
class Customer{
String code;
Address address;
}
I have found similar question on stack
Java jackson embedded object deserialization
but answer does not apply to my case. Also I only want to use Jackson library.
How can I map this json to Customer object?
You can put a #JsonUnwrapped annotation on the Address field in the customer class. Here is an example:
public class JacksonValue {
final static String JSON = "{\n"
+" \"code\":\"C001\",\n"
+" \"city\": \"Pune\",\n"
+" \"street\": \"ABC Road\"\n"
+"}";
static class Address {
public String city;
public String street;
#Override
public String toString() {
return "Address{" +
"city='" + city + '\'' +
", street='" + street + '\'' +
'}';
}
}
static class Customer {
public String code;
#JsonUnwrapped
public Address address;
#Override
public String toString() {
return "Customer{" +
"code='" + code + '\'' +
", address=" + address +
'}';
}
}
public static void main(String[] args) throws IOException {
final ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(JSON, Customer.class));
}
}
Output:
Customer{code='C001', address=Address{city='Pune', street='ABC Road'}}
What you need is a custom deserializer. Jackson How-To: Custom Deserializers
For your use case it could be something like this:
class CustomerDeserializer extends JsonDeserializer<Customer>
{
public Customer deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
JsonNode node = p.getCodec().readTree(p);
String code = node.get("code").asText();
String city = node.get("city").asText();
String street = node.get("street").asText();
Address adr = new Address(city, street);
return new Customer(code, adr);
}
}
Your JSON object for a customer should look like this:
{
"code":"C001",
"address":{
"city": "Pune",
"street": "ABC Road"
}
}
Without some additional transformation this json structure can't be mapped to two classes. Either write a class CustomerAddress that will be having all three fields from json and then create Address getAddress() and Customer getCustomer() in it or transform the json to nest the address information inside the customer field as suggested by #eztam.
public CustomerAddress {
private String code;
private String city;
private String street;
public Address getAddress() {
return new Address(city, street);
}
public Address getCustomer() {
return new Customer(code, this.getAddress());
}
}
Try this !!!
{
"code":"customer1",
"address":{
"type":"nested",
"properties":{
"city":"Hyderabad",
"street":"1000ftRoad"
}
}
}
I try to deserialize object that contains null-properties and have the JsonMappingException.
What I do:
String actual = "{\"#class\" : \"PersonResponse\"," +
" \"id\" : \"PersonResponse\"," +
" \"result\" : \"Ok\"," +
" \"message\" : \"Send new person object to the client\"," +
" \"person\" : {" +
" \"id\" : 51," +
" \"firstName\" : null}}";
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(new StringReader(json), PersonResponse.class); //EXCEPTION!
BUT: if to throw away "firstName = null" property - all works fine!
I mean pass the next string:
String test = "{\"#class\" : \"PersonResponse\"," +
" \"id\" : \"PersonResponse\"," +
" \"result\" : \"Ok\"," +
" \"message\" : \"Send new person object to the client\"," +
" \"person\" : {" +
" \"id\" : 51}}";
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(new StringReader(json), PersonResponse.class); //ALL WORKS FINE!
Question:
How to avoid this exception or to pledge Jackson ignore null-values during serialization?
Throws:
Message:
com.fasterxml.jackson.databind.MessageJsonException:
com.fasterxml.jackson.databind.JsonMappingException:
N/A (through reference chain: person.Create["person"]->Person["firstName"])
cause:
com.fasterxml.jackson.databind.MessageJsonException:
com.fasterxml.jackson.databind.JsonMappingException:
N/A (through reference chain: prson.Create["person"]->Person["firstName"])
cause: java.lang.NullPointerException
Sometimes this problem occurs when accidentally using a primitive type as return type of the getter of a non-primitive field:
public class Item
{
private Float value;
public float getValue()
{
return value;
}
public void setValue(Float value)
{
this.value = value;
}
}
Notice the "float" instead of "Float" for the getValue()-method, this can lead to a Null Pointer Exception, even when you have added
objectMapper.setSerializationInclusion(Include.NON_NULL);
If you don't want to serialize null values, you can use the following setting (during serialization):
objectMapper.setSerializationInclusion(Include.NON_NULL);
Hope this solves your problem.
But the NullPointerException you get during deserialization seems suspicious to me (Jackson should ideally be able to handle null values in the serialized output). Could you post the code corresponding to the PersonResponse class?
I also faced the same issue.
I just included a default constructor in the model class along with the other constructor with parameters.
It worked.
package objmodel;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class CarModel {
private String company;
private String model;
private String color;
private String power;
public CarModel() {
}
public CarModel(String company, String model, String color, String power) {
this.company = company;
this.model = model;
this.color = color;
this.power = power;
}
#JsonDeserialize
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
#JsonDeserialize
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#JsonDeserialize
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
#JsonDeserialize
public String getPower() {
return power;
}
public void setPower(String power) {
this.power = power;
}
}
Add JsonProperty annotation to your attribute in TO class, as below
#JsonProperty
private String id;