How to serialize ObjectId to JSON? - java

I want to serialize ObjectId of my Product class to JSON. I got the following JSON:
[{"name":"Play for Java: Covers Play 2","type":"Book","company":"Manning Publications","price":30.0,"imagePath":"public/images/play-for-java.png","rating":4.5,"category":"Computer","author":"Nicolas Leroux","publicationDate":1396224000000,"numPage":320,"_id":539da7a6370882f10d5c2777}]
You can notice that the "_id" didn't be properly serialized, it should be "539da7a6370882f10d5c2777" (with double quotes) and not just 539da7a6370882f10d5c2777.
Therefore, I have tried to implement my own ObjectIdSerializer as following:
import java.io.IOException;
import org.bson.types.ObjectId;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
#Override
public void serialize(ObjectId value, JsonGenerator jsonGen,SerializerProvider provider) throws IOException,
JsonProcessingException {
jsonGen.writeString(value.toString());
}
}
It gave me the different error: java.lang.String cannot be cast to org.bson.types.ObjectId (through reference chain: models.Book["_id"])
Here are my Product class and Book class:
Product.java
#JsonTypeInfo(use= JsonTypeInfo.Id.CLASS,property="_class")
public class Product {
#ObjectId #Id
#JsonSerialize(using = ObjectIdSerializer.class)
protected String id;
#JsonProperty("name")
protected String name;
#JsonProperty("type")
protected String type;
#JsonProperty("description")
protected String description;
#JsonProperty("company")
protected String company;
#JsonProperty("price")
protected float price;
#JsonProperty("imagePath")
protected String imagePath;
#JsonProperty("imageName")
protected String imageName;
#JsonProperty("rating")
protected float rating;
public Product() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// Getters and setters
...
Book.java
public class Book extends Product{
#JsonProperty("category")
private String category;
#JsonProperty("author")
private String author;
#JsonProperty("publicationDate")
private Date publicationDate;
#JsonProperty("numPage")
private int numPage;
public Book() {
}
// Getters and setters
...
Can you help me figure it out how can I properly serialize the ObjectId to JSON?

It looks like Jackson has been customized to serialize the string id field in a special way. That is probably a part of the integration with org.bson library.
The problem is that your deserializer is parametrized by the ObjectId type instead of String or plain Object. Try to change it as follows and also remove the #ObjectId annotation from the field declaration. Here is an example:
public class ObjectIdSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object value, JsonGenerator jsonGen,SerializerProvider provider) throws IOException {
jsonGen.writeString(value.toString());
}
}
You may also consider adopting the Jackson-Jongo provider class to fix the object id serialization for all the classes.

Related

How to deserialize json object into flatten format direct field?

I have the following structure:
public class User {
private Account account;
//constuctors, getters and setters
}
public class Account {
private String id;
private String description;
//constructor, getters and setters
}
When I performing the request I need to create the following JSON structure:
{
"account":
{
"id": "1",
"description": "Some description"
}
}
But I want to specify this information in a short way and ignore(left 'null') the 'description' field in the following way:
{
"account": "1" // I want to set directly the id field in the account object.
}
How may I do it? I tried #JsonCreator annotation and #JsonUnwrapped but without result.
You can use a custom deserializer
public class AccountFromIdDeserializer extends StdDeserializer<Account> {
public AccountFromIdDeserializer() { this(null);}
protected AccountFromIdDeserializer(Class<Account> type) { super(type);}
#Override
public Account deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
Account account = new Account();
account.setId(parser.getValueAsString());
return account;
}
}
And use on account node of User using #JsonDeserialize
public class User {
#JsonDeserialize(using = AccountFromIdDeserializer.class)
private Account account;
//constuctors, getters and setters
}
Finally I used #JsonCreator annotation and created two constructors:
#JsonCreator
public Account(#JsonProperty("id") String id, #JsonProperty("description") String description) {
this.id = id;
this.description = description;
}
#JsonCreator
public Account(String id) {
this.id = id;
}

How to exclude object property

I using Orika mapper to map two beans. i would like to exclude billingSummary.billableItems property while mapping. I am trying below option but it is not working.
Any help?
public class Cart {
private String id;
private String name;
private BillingSummary billingSummary;
private String address;
//with getter and setter methods
}
public class BillingSummary {
private String billingItem;
private String billingItemId;
private BillableItems billableItems;
...
// with getter setter methods
}
//FilteredCart is same as Cart.
public class FilteredCart {
private String id;
private String name;
private BillingSummary billingSummary;
private String address;
//with getter and setter methods
}
#Component
public class CartMapper extends ConfigurableMapper {
#Override
public void configure(MapperFactory mapperFactory) {
mapperFactory.classMap(Cart.class,FilteredCart.class).exclude("billingSummary.billableItems").byDefault().register();
}
}
What you can do is adding another mapping to the mapperFactory in order to define how you want to map the BillingSummary to itself. In this way, when mapping from Cart to FilteredCart, you can configure to exclude to map the billableItems.
Therefore, your CartMapper will look like this:
#Component
public class CartMapper extends ConfigurableMapper {
#Override
public void configure(MapperFactory mapperFactory) {
mapperFactory.classMap(BillingSummary.class, BillingSummary.class).exclude("billableItems").byDefault().register();
mapperFactory.classMap(Cart.class,FilteredCart.class).byDefault().register();
}
}

How to implement customized serialization feature in fasterxml

My JSON:
{
"name": "asdf",
"age": "15",
"address": {
"street": "asdf"
}
}
If street is null, with JsonSerialize.Inclusion.NON_NULL, I can get..
{
"name": "asdf",
"age": "15",
"address": {}
}
But I want to get something like this.. (when address is not null, it is a new/empty object. But street is null.)
{
"name": "asdf",
"age": "15"
}
I thought to have custom serialization feature like JsonSerialize.Inclusion.VALID_OBJECT.
Adding isValid() method in the Address class then if that returns true serialize else don't serialize.
But I don't know how to proceed further/which class to override. Is this possible or any other views on this? Please suggest.
Added classes
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
Customer customer = new Customer();
customer.setName("name");
customer.setAddress(new Address());
mapper.writeValue(new File("d:\\customer.json"), customer);
}
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class Customer {
private String name;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class Address {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
Note: I am not worrying about deserialization now. i.e, loss of address object.
Thanks in advance.
Customized JSON Object using Serialization is Very Simple.
I have wrote a claas in my project i am giving u a clue that how to Implement this in Projects
Loan Application (POJO Class)
import java.io.Serializable;
import java.util.List;
import org.webservice.business.serializer.LoanApplicationSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
#JsonSerialize(using=LoanApplicationSerializer.class)
public class LoanApplication implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private double amount;
private User borrowerId;
private String businessType;
private String currency;
private int duration;
private Date lastChangeDate;
private long loanApplicationId;
private String myStory;
private String productCategory;
private String purpose;
private Date startDate;
private String status;
private String type;
private String salesRepresentative;
Now LoanApplicationSerializer class that contains the Customization using Serialization Logic................
package org.ovamba.business.serializer;
import java.io.IOException;
import org.webservice.business.dto.LoanApplication;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class LoanApplicationSerializer extends JsonSerializer<LoanApplication> {
#Override
public void serialize(LoanApplication prm_objObjectToSerialize, JsonGenerator prm_objJsonGenerator, SerializerProvider prm_objSerializerProvider) throws IOException, JsonProcessingException {
if (null == prm_objObjectToSerialize) {
} else {
try {
prm_objJsonGenerator.writeStartObject();
prm_objJsonGenerator.writeNumberField("applicationId", prm_objObjectToSerialize.getLoanApplicationId());
prm_objJsonGenerator.writeStringField("status", prm_objObjectToSerialize.getStatus());
prm_objJsonGenerator.writeNumberField("amount", prm_objObjectToSerialize.getAmount());
prm_objJsonGenerator.writeNumberField("startdate", prm_objObjectToSerialize.getStartDate().getTime());
prm_objJsonGenerator.writeNumberField("duration", prm_objObjectToSerialize.getDuration());
prm_objJsonGenerator.writeStringField("businesstype", prm_objObjectToSerialize.getBusinessType());
prm_objJsonGenerator.writeStringField("currency", prm_objObjectToSerialize.getCurrency());
prm_objJsonGenerator.writeStringField("productcategory", prm_objObjectToSerialize.getProductCategory());
prm_objJsonGenerator.writeStringField("purpose", prm_objObjectToSerialize.getPurpose());
prm_objJsonGenerator.writeStringField("mystory", prm_objObjectToSerialize.getMyStory());
prm_objJsonGenerator.writeStringField("salesRepresentative", prm_objObjectToSerialize.getSalesRepresentative());
} catch (Exception v_exException) {
//ExceptionController.getInstance().error("Error while Serializing the Loan Application Object", v_exException);
} finally {
prm_objJsonGenerator.writeEndObject();
}
}
}
}
Hope This may help u alot. Thanks..
You can do it by annotating your class with #JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
Example:
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public myClass{
// attributes and accessors
}
You can find some useful informations at Jackson faster xml

Object properties lowercased when reading as JSONObject

I have the following problem. I'm reading a list of records from my MySQL database with Hibernate template, and then I need to modify the structure so I'm JSONObject and JSONArray (using I guess the official library : http://www.json.org/java/). If I'm using the List as a server response, records fields are properly named (thanks to #JsonProperty annotation used). But if I'm trying to create a JSONObject out of this List element, I'm getting all my fields starting with small letter, which breaks my UI.
This is my 'Task' model used :
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
#JsonAutoDetect
#JsonIgnoreProperties(ignoreUnknown = true)
#Entity
#Table(name="tasks")
public class Task {
#Id
#GeneratedValue
#Column(name="Id")
private int Id;
#Column(name="Name", nullable=false)
private String Name;
#JsonProperty("Id")
public int getId() {
return Id;
}
#JsonProperty("Id")
public void setId(int id) {
this.Id = id;
}
#JsonProperty("Name")
public String getName() {
return Name;
}
#JsonProperty("Name")
public void setName(String name) {
this.Name = name;
}
}
and here's the code used for getting records from the DB (stripped of all the unnecessary parts):
public List<Task> getEvents() {
DetachedCriteria criteria = DetachedCriteria.forClass(Task.class);
return hibernateTemplate.findByCriteria(criteria);
}
private static JSONArray read() throws JSONException{
List<Task> list = getEvents();
Iterator<Task> listIterator = list.iterator();
JSONArray ret = new JSONArray();
String parentId;
while(listIterator.hasNext()){
Task task = listIterator.next();
JSONObject taskJSON = new JSONObject(task);
ret.put(taskJSON);
}
}
As you can see in my server response, all fields names start with small letter :
{"id":18,"name":"Release"}
Any ideas how to override this ?
Your class is overannotated, and breaks Java code conventions.
The minimum required is as follows. Everything else you've added is done by default.
#Entity
#Table(name="tasks")
public class Task {
#Id
#GeneratedValue
#Column(name="Id")
#JsonProperty("Id")
private int id;
#Column(name="Name", nullable=false)
#JsonProperty("Name")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
To serialise your class all you should need is the ObjectMapper class
String json = new ObjectMapper().writeValueAsString(getEvents());
The output of which should look like:
[{"Id":18,"Name":"Build"}, {"Id":19,"Name":"Release"}]
I would discourage using capitalised property names if possible as it goes against general code conventions.
The JSON.org API is intended for very simple serialization/deserialization, it can't do what your looking for. Having said that, the majority of your annotations are actually from Jackson, which can do what your trying to accomplish.
You already have the POJOs properly annotated for Jackson, so return a JSON string conforming to them, serialize using an ObjectMapper:
final List<Task> list = getEvents();
final ObjectMapper mapper = new ObjectMapper();
final String json = mapper.writeValueAsString(list);

Duplicate json property when converting java object to json string using jackson

I have Pojo object, with getAsJson function to return Json string for this object.
I use JsonProperty to define json properties in this object.
Use writeValueAsString of ObjectMapper to write json string for this object.
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
#JsonIgnoreProperties(ignoreUnknown=true)
public class LogLikeArticleDetail extends BaseObject {
private static final long serialVersionUID = -2018373118257019033L;
#JsonProperty("LikeArticleGUId")
private String likeArticleGUId;
#JsonProperty("UserId")
private String userID;
#JsonProperty("UserName")
private String userName;
#JsonProperty("IP")
private String ip;
#JsonProperty("OS")
private String os;
#JsonProperty("UserAgent")
private String userAgent;
#JsonProperty("WebsiteCode")
private String websiteCode;
#JsonProperty("ArticleId")
private String articleID;
#JsonProperty("ATitle")
private String aTitle;
#JsonProperty("CateAlias")
private String cateAlias;
#JsonProperty("LikeStatus")
private String likeStatus;
#JsonProperty("TimeStamp")
private Date timeStamp;
//get, set....
//....
#JsonIgnore
public String getAsJSON() throws JsonGenerationException, JsonMappingException, IOException{
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this) ;
}
}
Now, i get result
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
Calendar calendar = Calendar.getInstance();
LogLikeArticleDetail logLikeArticle = new LogLikeArticleDetail("1","2","3","4","5","6","7","8","what thing \"nothing\" show","10","11",calendar.getTime());
System.out.println(logLikeArticle.getAsJSON());
}
But the result's duplicated properties:
{"LikeArticleGUId":"1","UserId":"2","UserName":"3","IP":"4","OS":"5","UserAgent":"6","WebsiteCode":"7","ArticleId":"8","ATitle":"what thing \"nothing\" show","CateAlias":"10","LikeStatus":"11","TimeStamp":1352256727062,"_likeArticleGUId":"1","websiteCode":"7","likeStatus":"11","userID":"2","userName":"3","ip":"4","os":"5","userAgent":"6","articleID":"8","aTitle":"what thing \"nothing\" show","cateAlias":"10","timeStamp":1352256727062}
Show me what's occur in this problem ?
So i do follow:
how to specify jackson to only use fields - preferably globally
I add
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
before
public class LogLikeArticleDetail extends BaseObject
and the result that i want.
So can another solve that in getAsJson() function like:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibilityChecker(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
return mapper.writeValueAsString(this) ;
Thanks for #Sean Carpenter 's question and #kmb385 answer in link above.
You can also do this per POJO using annotations. Add this string to the top of your class you'd like no auto detection on:
#JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY,
getterVisibility=JsonAutoDetect.Visibility.NONE,
setterVisibility=JsonAutoDetect.Visibility.NONE,
creatorVisibility=JsonAutoDetect.Visibility.NONE)
For example:
#JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility=JsonAutoDetect.Visibility.NONE,
setterVisibility=JsonAutoDetect.Visibility.NONE, creatorVisibility=JsonAutoDetect.Visibility.NONE)
class Play {
#JsonProperty("Name")
private String name;
#JsonProperty("NickName")
private String nickName;
public Play(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
}
This will return the properties I've defined and not auto-detect the field names and add them to my returned JSON result.
We can also use the #JsonProperty("Name") annotation directly on the getters to avoid duplication.
It is actually not an issue. So, over here what happened was Jackson library was unable to match those fields automatically (there is no assumption of case unification), so you end up with twice the properties you expect.
The simple fix for this issue is to just add annotations to either getters/setters (either is fine.)
#JsonProperty("UserName")
public String getUserName() {
return this.userName;
}
This issue was also raised in Jackson Github repo. You can find the answer in the following link.
https://github.com/FasterXML/jackson-databind/issues/1609

Categories

Resources