I am trying to use Jackson2.0.0 with Spring3.1 so that I can use the jackson-Module-Hibernate. I have followed the steps as described here http://blog.pastelstudios.com/2012/03/12/spring-3-1-hibernate-4-jackson-module-hibernate/. All this seems to work fine, but when I try using JSON Views so that the JSON contains only the fields in the view it does not work.
The active view is always null. How do make the view active? I have tried for a day now with no luck...any help at all will be greatly appreciated. Thanks in advance.
Below is the relavant code.
Here is the Mapper
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
Hibernate4Module hm = new Hibernate4Module();
registerModule(hm);
hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, true);
}
}
Here is the view class
public class DiffViews {
public static class Public { }
}
Here is the POJO where I use the view
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
#JsonIgnoreProperties(ignoreUnknown=true)
#Entity
public class Premium implements java.io.Serializable {
#JsonView(DiffViews.Public.class)
private String sequence;
#JsonView(DiffViews.Public.class)
#Column(name = "SEQUENCE", nullable = false, length = 4)
public String getSequence() {
return this.sequence;
}
public void setSequence(String sequence) {
this.sequence = sequence;
}
#Column(name = "NAME", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
)
In my spring controller
#RequestMapping("/cartonPremium")
public void listAll(
#RequestParam("page") int page, #RequestParam("rows") int maxResults,
#RequestParam("sidx") String sortKey, #RequestParam("sord") String sortOrder, HttpServletResponse response) {
HibernateAwareObjectMapper mapper = new HibernateAwareObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.enable(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS);
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION );
JqgridResponse<Premium> gridResponse = new JqgridResponse<Premium>();
gridResponse.setRows(premiumList);
gridResponse.setRecords(""+premiumList.size());
gridResponse.setTotal(""+premiumList.size());
gridResponse.setPage(""+page);
try {
ObjectWriter objWriter= mapper.writerWithView(DiffViews.Public.class);
Class<?> xxx = mapper.getSerializationConfig().getActiveView();
objWriter.writeValue(response.getOutputStream(), gridResponse);
//mapper.writerWithView(DiffViews.Public.class).writeValue(response.getOutputStream(), gridResponse);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
My spring config
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.creata.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.creata.json.HibernateAwareObjectMapper" />
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
I have finally found my error... I was returning my domain POJO in a wrapper called JqgridResponse and I had not added the #JsonView annotation on the fields in this wrapper class. So all is good the json view is not getting ignored.
Related
In my Spring Boot app I have Entity like that:
#Entity
#Table(name = "UserENT")
public class User implements Serializable {
#Id
private String id;
private Data data;
...
I would like to achieve that object Data will be stored in DB in json format. But it will be mapped on Data object when selecting from DB.
Thanks for any advice.
You can implement a javax.persistence.AttributeConverter, as in:
public class DataJsonConverter implements AttributeConverter<Data, String> {
private ObjectMapper objectMapper = ...;
#Override
public String convertToDatabaseColumn(Data data) {
try {
return objectMapper.writeValueAsString(data);
} catch (JsonProcessingException e) {
throw new RuntimeException("Could not convert to Json", e);
}
}
#Override
public Data convertToEntityAttribute(String json) {
try {
return objectMapper.readValue(json, Data.class);
} catch (IOException e) {
throw new RuntimeException("Could not convert from Json", e);
}
}
}
You can then use it by annotating your field:
#Convert(converter = DataJsonConverter.class)
private Data data;
Is there any way to add serialization for list implementing class having custom attributes?
I am working on Rest service using Spring-boot 1.3. I have to return JSON response as Paged-List or Normal-List, depend on request on Controller. So, I have to keep return type of controller method as generic public List<Employee> getEmployees(int departmentId)
I am implementing list as below (using generics to use for different object lists)
public class PagedList<E> implements List<E> {
private List<E> list;
private long totalRecords; //Getter-setters are added
public PagedList(List<E> list) {
super();
this.list = list;
}
public PagedList(List<E> list, long totalRecords) {
super();
this.list = list;
this.totalRecords = totalRecords;
}
#Override
public boolean add(E element) {
return this.list.add(element);
}
//All other List abstract methods implemented same as above using this.list
}
Added JsonSerializer for same: public class PagedListSerializer extends JsonSerializer<PagedList> with serialization logic in serialize() method. Which is registered using spring-boot jackson customization :
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializerByType(PagedList.class, new PagedListSerializer());
return builder;
}
When I try to return PagedList<Employee>(list, 1000), I am not able to get following response. Its returning same as of normal list. Not executing custom serialization. How to get following paged response?
{
list : [{employeeId: "1", name: "John" }, ... ],
totalRecords : 1000
}
You probably don't need custom deserializer to get this json. Just add #JsonFormat(shape = JsonFormat.Shape.OBJECT) annotation to your class:
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public static class PagedList<E> implements List<E> {
#JsonProperty
private List<E> list;
#JsonProperty // no need for this if you have getter-setters
private long totalRecords;
#JsonIgnore
#Override
public boolean isEmpty() {
return false;
}
...
Here is full demo: https://gist.github.com/varren/35c4ede769499b1290f98e39a2f85589
Update after comments:
I think Spring uses Jacksons return mapper.writerFor(List.class).writeValueAsString(new MyList()); Here is demo:
#RestController
#RequestMapping(value="/")
public static class MyRestController {
private static final ObjectMapper mapper = new ObjectMapper();
//returns [] for both 0 and 1
#RequestMapping(value="test", method = RequestMethod.GET)
public List test(#RequestParam int user) {
return user == 0 ? new ArrayList(): new MyList();
}
//returns [] for 0 and expected custom {"empty": true} for 1
#RequestMapping(value="testObj", method = RequestMethod.GET)
public Object testObj(#RequestParam int user) {
return user == 0 ? new ArrayList(): new MyList();
}
// returns expected custom {"empty": true}
#RequestMapping(value="testMyList", method = RequestMethod.GET)
public MyList testMyList() {
return new MyList();
}
// returns expected custom {"empty": true}
#RequestMapping(value="testMyListMapper", method = RequestMethod.GET)
public String testMyListMapper() throws JsonProcessingException {
return mapper.writeValueAsString(new MyList());
}
// returns []
#RequestMapping(value="testMyListMapperListWriter", method = RequestMethod.GET)
public String testMyListMapperListWriter() throws JsonProcessingException {
return mapper.writerFor(List.class).writeValueAsString(new MyList());
}
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public static class MyList extends ArrayList {}
So you have to Option 1) return Object instead of List or Option 2) register custom serialifer for List (and not for PageList) builder.serializerByType(List.class, new PagedListSerializer()); like this:
public class PagedListSerializer extends JsonSerializer<List> {
#Override
public void serialize(List valueObj, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
if (valueObj instanceof PagedList) {
PagedList value = (PagedList) valueObj;
gen.writeStartObject();
gen.writeNumberField("totalRecords", value.getTotalRecords());
gen.writeObjectField("list", value.getList());
gen.writeEndObject();
}else{
gen.writeStartArray();
for(Object obj : valueObj)
gen.writeObject(obj);
gen.writeEndArray();
}
}
}
You can Create your customObject Mapper and use your serializer there.
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="custom.CustomObjectMapper"/>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
I have a class with lots of attributes which are required for server side logic, but a few of those are required for UI. Now when I am creating a json from the class, all the attributes are written to json. I want to ignore some values only when it is converted to json. I Tried with #JsonIgnore. But it is not working.
My Class Is
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties(ignoreUnknown = true)
public class Student {
#JsonProperty("id")
protected Integer id;
#JsonProperty("name")
protected String name;
/**
* This field I want to ignore in json.
* Thus used #JsonIgnore in its getter
*/
#JsonProperty("securityCode")
protected String securityCode;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonIgnore
public String getSecurityCode() {
return securityCode;
}
public void setSecurityCode(String securityCode) {
this.securityCode = securityCode;
}
}
And I am writing this using
public static StringBuilder convertToJson(Object value){
StringBuilder stringValue = new StringBuilder();
ObjectMapper mapper = new ObjectMapper();
try {
stringValue.append(mapper.writeValueAsString(value));
} catch (JsonProcessingException e) {
logger.error("Error while converting to json>>",e);
}
return stringValue;
}
My Expected json should contain only :
id:1
name:abc
but what I am getting is
id:1
name:abc
securityCode:_gshb_90880..some_value.
What is wrong here, please help
Your #JsonProperty annotation overrides #JsonIgnore annotation. Remove #JsonProperty from securityCode and your desired json output will be produced.
If you want more advanced ignoring / filtering please take a look at:
#JsonView : http://wiki.fasterxml.com/JacksonJsonViews
#JsonFilter : http://wiki.fasterxml.com/JacksonFeatureJsonFilter
Ok, so first off here's the JSON that's returning from my web service. I'm trying to deserialize it into pojos after an asynchronous query in a ResponseHandler in my Android ContentProvider.
{"exampleList" : [{
"locationId" : "00001" ,
"owners" : [
{
"paidID" : { "$oid" : "50a9c951300493f64fbffdb6"} ,
"userID" : { "$oid" : "50a9c951300493f64fbffdb6"}
} ,
{
"paidID" : { "$oid" : "50a9c951300493f64fbffdb7"} ,
"userID" : { "$oid" : "50a9c951300493f64fbffdb7"}
}
]
}]}
At first, I was confused about the problem I was seeing, since I use the same Jackson-annotated beans for my web service as I do in my Android app--but then I realized that the owners object was never getting sent in the sample JSON to my web service (it skips the POJOs on my web service and gets added into the documents in mongoDB through atomic updates from the DAO).
So OK. Up to now, Jackson wasn't having to handle the owners object, and now that it is it is choking on it, namely:
JsonMappingException: Can not deserialize instance of java.lang.String out of
START_OBJECT token at [char position where you can find "userID" and "paidID"]
through reference chain [path to my Jackson bean which contains the owners class]
My Jackson bean has a wrapper, which is what that "exampleList" is all about:
public class Examples extends HashMap<String, ArrayList<Example>> {
}
And then the actual Example class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Example implements Comparable<Example> {
#ObjectId #Id
private String id;
#JsonProperty(Constants.Example.location)
private String location;
#JsonProperty(Constants.Example.OWNERS)
private List<Owners> owners;
public int compareTo(Example _o) {
return getId().compareTo(_o.getId());
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public List<Example.Owners> getOwners() {
return owners;
}
public void setOwners(List<Example.Owners> owners) {
this.owners = owners;
}
public Example() {
}
#JsonCreator
public Example(#Id #ObjectId String id) {
this.id = id;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Owners implements Comparable<Owners> {
#JsonProperty(Constants.Example.USERID)
private String userID;
#JsonProperty(Constants.Example.PAIDID)
private String paidID;
public Owners() {
}
public int compareTo(Owners _o) {
return getUserID().compareTo(_o.getUserID());
}
#ObjectId
public String getUserID() {
return userID;
}
#ObjectId
public void setUserID(String userID) {
this.userID = userID;
}
#ObjectId
public String getPaidID() {
return paidID;
}
#ObjectId
public void setPaidID(String paidID) {
this.paidID = paidID;
}
}
}
And finally, the code in the ResponseHandler where this is all failing (the 2nd line produces the JsonMappingException):
objectMapper = MongoJacksonMapperModule.configure(objectMapper);
mExamples = objectMapper.readValue(jsonParser, Examples.class);
I have a feeling the issue is that Jackson still doesn't know how to map those $oid, which are the mongoDB ObjectIds. The MongoJacksonMapper library is supposed to help that by providing the #ObjectId annotation and a way to configure the ObjectMapper to use that library, but it still isn't working. For some reason, it's still looking for the userID or paidID as a String, not an ObjectId. Any ideas?
Another alternative is
com.fasterxml.jackson.databind.ser.std.ToStringSerializer.
#Id
#JsonSerialize(using = ToStringSerializer.class)
private final ObjectId id;
This will result in:
{
"id": "5489f420c8306b6ac8d33897"
}
For future users: Use a custom jackson deserializer to convert $oid back to ObjectId.
public class ObjectIdDeserializer extends JsonDeserializer<ObjectId> {
#Override
public ObjectId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode oid = ((JsonNode)p.readValueAsTree()).get("$oid");
return new ObjectId(oid.asText());
}
}
How to use:
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("ObjectId", new Version(1, 0, 0, null, null, null));
mod.addDeserializer(ObjectId.class, new ObjectIdDeserializer());
mapper.registerModule(mod);
YourClass obj = mapper.readValue("{your json with $oid}", YourClass.class);
My code had at least two problems that were pretty tough to track down answers to online, so I'll make sure to link here. Basically, child classes need a constructor in the parent class that calls Jackson's readValue() to map the child. As far as mongoDB $oid's go, you should create a separate MongoId class to represent these mongo objects, and follow a similar pattern as with the child class to map the data when it comes in for deserialization. Here's a blog post I found that describes this well and provides some examples.
Jackson does not know how to serialize an ObjectId. I tweaked Arny's code to serialize any ObjectId and provide this working example:
public class SerialiserTest {
private ObjectMapper mapper = new ObjectMapper();
public static class T {
private ObjectId objectId;
public ObjectId getObjectId() {
return objectId;
}
public void setObjectId(ObjectId objectId) {
this.objectId = objectId;
}
}
#Test
public final void serDeser() throws IOException {
T t = new T();
t.setObjectId(new ObjectId());
List<T> ls = Collections.singletonList(t);
String json = mapper.writeValueAsString(ls);
System.out.println(json);
SimpleModule mod = new SimpleModule("ObjectId", new Version(1, 0, 0, null, null, null));
mod.addDeserializer(ObjectId.class, new ObjectIdDeserializer());
mapper.registerModule(mod);
JavaType type = mapper.getTypeFactory().
constructCollectionType(List.class, T.class);
List<?> l = mapper.readValue(json, type);
System.out.println(l);
}
}
public class ObjectIdDeserializer extends JsonDeserializer<ObjectId> {
#Override
public ObjectId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode n = (JsonNode)p.readValueAsTree();
return new ObjectId(n.get("timestamp").asInt(), n.get("machineIdentifier").asInt(), (short) n.get("processIdentifier").asInt(), n.get("counter").asInt());
}
}
There's an even easier way documented here which was a lifesaver for me. Now you can use the ObjectId in Java but when you go to/from JSON it'll be a String.
public class ObjectIdJsonSerializer extends JsonSerializer<ObjectId> {
#Override
public void serialize(ObjectId o, JsonGenerator j, SerializerProvider s) throws IOException, JsonProcessingException {
if(o == null) {
j.writeNull();
} else {
j.writeString(o.toString());
}
}
}
And then in your beans:
#JsonSerialize(using=ObjectIdJsonSerializer.class)
private ObjectId id;
I did it like this:
#Configuration
public class SpringWebFluxConfig {
#Bean
#Primary
ObjectMapper objectMapper() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializerByType(ObjectId.class, new ToStringSerializer());
builder.deserializerByType(ObjectId.class, new JsonDeserializer() {
#Override
public Object deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
Map oid = p.readValueAs(Map.class);
return new ObjectId(
(Integer) oid.get("timestamp"),
(Integer) oid.get("machineIdentifier"),
((Integer) oid.get("processIdentifier")).shortValue(),
(Integer) oid.get("counter"));
}
});
return builder.build();
}
}
i use the playframework and tried to deserialize some json into a java object.
It worked fine, exept the relationship in the model. I got the following exception
enter code hereorg.codehaus.jackson.map.JsonMappingException: Can not
instantiate value of type [simple type, class models.Job] from JSON
String; no single-String constructor/factory method (through reference
chain: models.Docfile["job"])
i thought jackson in combination with play could do that:
this is the json
{"name":"asd","filepath":"blob","contenttype":"image/png","description":"asd","job":"1"}
and this my code, nothing special:
public static Result getdata(String dataname) {
ObjectMapper mapper = new ObjectMapper();
try {
Docfile docfile = mapper.readValue((dataname), Docfile.class);
System.out.println(docfile.name);
docfile.save();
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return ok();
}
Hope there is help for me, thanks
Markus
UPDATE:
Docfile Bean:
package models;
import java.util.*;
import play.db.jpa.*;
import java.lang.Object.*;
import play.data.format.*;
import play.db.ebean.*;
import play.db.ebean.Model.Finder;
import play.data.validation.Constraints.*;
import play.data.validation.Constraints.Validator.*;
import javax.persistence.*;
import com.avaje.ebean.Page;
#Entity
public class Docfile extends Model {
#Id
public Long id;
#Required
public String name;
#Required
public String description;
public String filepath;
public String contenttype;
#ManyToOne
public Job job;
public static Finder<Long,Docfile> find = new Model.Finder(
Long.class, Docfile.class
);
public static List<Docfile> findbyJob(Long job) {
return find.where()
.eq("job.id", job)
.findList();
}
public static Docfile create (Docfile docfile, Long jobid) {
System.out.println(docfile);
docfile.job = Job.find.ref(jobid);
docfile.save();
return docfile;
}
}
Either you change your JSON in order to describe your "job" entity :
{
"name":"asd",
"filepath":"blob",
"contenttype":"image/png",
"description":"asd",
"job":{
"id":"1",
"foo", "bar"
}
}
or you create a constructor with a String parameter in your Job bean:
public Job(String id) {
// populate your job with its id
}
when limited time +ee: +jax-rs && +persistence, +gson; I have solved it then as:
#Entity
#XmlRootElement
#Table(name="element")
public class Element implements Serializable {
public Element(String stringJSON){
Gson g = new Gson();
Element a = g.fromJson(stringJSON, this.getClass());
this.setId(a.getId());
this.setProperty(a.getProperty());
}
public Element() {}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
...
}