Jersey - Serializing a POJO with json - nested objects will be ignored - json

HiAll,
i have the following problem in development branch:
If i use the jersey REST service to serialize the POJO containig a list of the nested other POJO, then this nested POJO will be NOT serialized. This problem is reproducible only in branch.
If i prototype using POJO structur liked in branch, i have no problem.
The Details:
The POJO (Domain):
public class Article {
private int id;
private int name;
// getter & setter
}
The POJO ( DTO)
public class DTO {
private List<Article> articles;
private String message;
public DTO {
articles= new ArrayList<>();
}
getArticles() {
return articles;
}
getArticlesCount() {
return articles.size();
}
public void getMessage() {
return message;
}
public String getMessage() {
return message;
}
....
}
It will be created the 1 article with id = 1 and name "firstArticle" the
The results of serialization after service call to find all articles:
in branch
*{"message":"1 article found","articlesCount":1
}*
in prototype
{
{"message":"1 article found","articlesCount":1[{"id":1,"name":"firstArticle"]}
}
i have no idia what's happened. I checked out all settings (web.xml, jersey version, etc.)

If you use Jackson JSON library, your POJO will be automatically handled by Jackson JSON lib to convert between JSON and POJO. You also need to add the following lines in your web.xml:
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Then in your Jersey resource class, use #Produces and #Consumes annotation to indicate the data format is JSON:
#Path("/myResource")
#Produces("application/json")
public class SomeResource {
#GET
public String doGetAsJson() {
...
}
}

Follow the above answer to set up Jersey POJO mappping, and then try the following nested POJO example.
Article class:
public class Article {
private int id;
private String name;
public Article(int id, String name) {
this.id = id;
this.name = name;
}
//getter and setter ...
}
The Book class that contains the Article class:
public class Book {
private List<Article> articles;
private String message;
private int articleCount;
public List<Article> getArticles() {
return articles;
}
public void setArticles(List<Article> articles) {
this.articles = articles;
setArticleCount(articles.size());
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getArticleCount() {
return articleCount;
}
public void setArticleCount(int articleCount) {
this.articleCount = articleCount;
}
}
Finally in the Jersey resource class NestedPojoResource:
#Path("/nested-pojo")
public class NestedPojoResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Book getNestedPojo() {
Book book = new Book();
List<Article> articles = new ArrayList<Article>();
articles.add(new Article(1, "firstArticle"));
articles.add(new Article(2, "secondArticle"));
book.setArticles(articles);
book.setMessage(book.getArticleCount() + " articles found");
return book;
}
}
When you go to http://example.com/myappname/nested-pojo, you will see the correct JSON output which contains the nested POJO fields:
{"articles":[{"id":1,"name":"firstArticle"},{"id":2,"name":"secondArticle"}],"message":"2 articles found","articleCount":2}

Related

How to deserialize json to an abstract class in spring-boot

In my Application i have something like this.
public class Question{}
public class MCQ extends Question{}
public class TrueAndFalse Question{}
public class Match Question{}
and in my RestController i have a service that adds question.
#RequestMapping(value = "/game/question/add", method = RequestMethod.POST)
public Question addQuuestion(#RequestParam("gameId") long id, #RequestBody Question question)
But i get an error when i try to call this service as i send json file with different structures one for MCQ, TrueAndFalse and Match.
so is it possible to deserialize the received json to Question abstract class.
And thanks in advance.
You can create a custom deserializer which will create Question instances based on json payload properties.
For example if the Question class looks like this:
public class Question {
private final String name;
#JsonCreator
Question(#JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
}
And sub-class TrueAndFalse:
public class TrueAndFalse extends Question {
private final String description;
#JsonCreator
TrueAndFalse(#JsonProperty("name") String name,
#JsonProperty("description") String description) {
super(name);
this.description = description;
}
public String getDescription() {
return description;
}
}
Then you can create a deserializer, which will create an instance of TrueAndFale sub-class by checking if it has description property:
public class QuestionDeserializer extends JsonDeserializer<Question> {
#Override
public Question deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
ObjectCodec codec = p.getCodec();
JsonNode tree = codec.readTree(p);
if (tree.has("description")) {
return codec.treeToValue(tree, TrueAndFalse.class);
}
// Other types...
throw new UnsupportedOperationException("Cannot deserialize to a known type");
}
}
And afterwards, make sure to register it on the object mapper:
#Configuration
public class ObjectMapperConfiguration {
#Bean
public ObjectMapper objectMapper() {
SimpleModule module = new SimpleModule();
module.addDeserializer(Question.class, new QuestionDeserializer());
return new ObjectMapper().registerModules(module);
}
}

How to ignore attributes while serializing a class by ObjectMapper

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

Marshalling java Lists with JAXB / JSON for jqGrid using Jettison or Jackson

I am implementing a generic java POJO wrapper for jqGrid consumption, using JAXB and JSON. This is a CXF service so my marshaller libraries of choice are either Jettison or Jackson:
#XmlRootElement(name = "response")
public class JQGridResponseWrapper<T> {
private PaginatedResults<T> results;
public JQGridResponseWrapper() {
}
public JQGridResponseWrapper(PaginatedResults<T> results) {
this.results = results;
}
#XmlElementWrapper(name = "records")
#XmlElement(name = "record")
public List<T> getRecords() {
return results.getRecords();
}
#XmlElement(name = "pager")
public Pager getPager() {
return results.getPager();
}
}
Here's a sample POJO to be wraped by the generic wrapper:
#XmlRootElement
public class Note {
private Long id;
private String subject;
private String description;
private Project project;
public Note() {}
public Note(Long id, String subject, String description, Project project) {
this.id = id;
this.subject = subject;
this.description = description;
this.project = project;
}
#XmlElement(name="noteId")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
When marshaling to XML, everything works fine, all types are correctly mapped, and I get a parent <records> element containing an array of <record> elements. But when marshaling to JSON (the project requirement), the 'record' element is unnamed, which makes jqGrid choke:
{"records":[
{"subject":"subject aaa",
"description":"Description dsifj ofdisjo",
"project":{
"projectCode":"HWIIA",
"description":"project description",
"brand":null,
"projectId":101
},
"noteId":201
},
{"subject":"subject bbb",
"description":"Description odisfj doisjf odsijf",
"project":{
"projectCode":"HWIIA",
"description":"project description",
"brand":null,
"projectId":101
},
"noteId":202
},
{"subject":"subject ccc",
"description":"Description oijgf gfoij jgifif",
"project":{
"projectCode":"HWIIA",
"description":"project description",
"brand":null,
"projectId":101
},
"noteId":203
}
],
"pager"{
"recordsPerPage":10,
"currentPage":1,
"fromRecord":1,
"toRecord":3,
"totalRecords":3,
"totalPages":1}}
I need to get a name for each record in the records array. Is there a simple way to make this work, either with Jettion or Jackson? I searched and searched the web but couldn't find a straighforward solution for my target marshaler libraries. I did see some answers for MOXY, but it is problematic for me to change libraries at this point. Any help greatly appreciated.

Jackson Mapper Serialize/Deserialize ObjectId

My POJO is :
import org.jongo.marshall.jackson.id.Id;
public class User {
#Id
private String id;
private String name;
private int age;
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
I get user from mongo database and want to output him into a file with jackson mapper
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter().writeValue(new File("c:/user.txt"), user);
and I get something like this in my file
{
"name" : "John",
"age" : 23,
"_id" : {
"time" : 1358443593000,
"inc" : 660831772,
"machine" : 2028353122,
"new" : false,
"timeSecond" : 1358443593
}
}
I need id field to be stored into a file as string because when i deserialize this object my id field in pojo looks something like this
{
"time":1358443593000,
"inc":660831772,
"machine":2028353122,
"new":false,
"timeSecond":1358443593
}
Any help will be apreciated
Answering my own question. Found solution here Spring 3.2 and Jackson 2: add custom object mapper
I needed custom object mapper and ObjectId serializer.
public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
#Override
public void serialize(ObjectId value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(value.toString());
}
}
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
SimpleModule module = new SimpleModule("ObjectIdmodule");
module.addSerializer(ObjectId.class, new ObjectIdSerializer());
this.registerModule(module);
}
}
I found an easy attempt using springboot 2.5.4.
Just by adding a Jackson2ObjectMapperBuilderCustomizer bean will do the trick.
#Configuration
public class JacksonMapperConfiguration
{
#Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> builder.serializerByType(ObjectId.class, new ToStringSerializer());
}
}
Here is a simple solution for serialization if you don't have a model for the object being stored:
client.getDatabase("db").getCollection("collection").find().onEach { it["_id"] = it["_id"].toString() }
"onEach" is a kotlin function. If you use Java, then change it to a simple foreach.
It's not efficient to iterate over the entire list just for the id. Only use it for small lists where performance is less important than short code.

jersey (+ jackson) map field serialization

I have a simple jersey web service and I'd like to consume / produce objects that contain map fields, like
#XmlElement
private Map<String,String> properties;
if this string goes into the web service,
{ properties: { key1: val1, key2: val2 )}
the properties field is deserialized as null with no errors. the same JSON goes in and out of GSON no problems, and in the short term I solved this by having jersey consume produce strings and using GSON to serialize / deserialize the JSON.
any ideas?
One option is to use annotated classes. So for instance a user might be represented by the following data.
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "user")
public class User {
private int uid;
public int user_id;
public String user_name;
public String email;
public URI image_url;
public List<User> friends;
public boolean admin;
public User() {
...
}
public User(final int userid) {
// Find user by id
}
}
If you return the User object as in the following piece of code, then jaxb will automatically serialize the List as a JSON list etc etc....
#GET
#Path("/{userid}")
#Produces("application/json", "application/xml")
public User showUser(#PathParam("userid") final int userid) {
return new User(userid);
}
Jersey uses JAXB for serialization. JAXB can not serialize a Map as there is no XML type for Java type Map. Also, Map is an interface and JAXB does not like interfaces.
If you are using JAXBJackson bridge to marshal, you will run into issue.
You will need to create an adapter like below and annotate your Map property with
#XmlJavaTypeAdapter(MapAdapter.class)
private Map<String,String> properties;
#XmlSeeAlso({ Adapter.class, MapElement.class })
public class MapAdapter<K,V> extends XmlAdapter<Adapter<K,V>, Map<K,V>>{
#Override
public Adapter<K,V> marshal(Map<K,V> map) throws Exception {
if ( map == null )
return null;
return new Adapter<K,V>(map);
}
#Override
public Map<K,V> unmarshal(Adapter<K,V> adapter) throws Exception {
throw new UnsupportedOperationException("Unmarshalling a list into a map is not supported");
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="Adapter", namespace="MapAdapter")
public static final class Adapter<K,V>{
List<MapElement<K,V>> item;
public Adapter(){}
public Adapter(Map<K,V> map){
item = new ArrayList<MapElement<K,V>>(map.size());
for (Map.Entry<K, V> entry : map.entrySet()) {
item.add(new MapElement<K,V>(entry));
}
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="MapElement", namespace="MapAdapter")
public static final class MapElement<K,V>{
#XmlAnyElement
private K key;
#XmlAnyElement
private V value;
public MapElement(){};
public MapElement(K key, V value){
this.key = key;
this.value = value;
}
public MapElement(Map.Entry<K, V> entry){
key = entry.getKey();
value = entry.getValue();
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
}