Custom object display in JSON - java

I'm using only jersey and not jackson to create a REST api. I've two model objects,
public class Course {
private int id;
private String name;
private Teacher teacher;
}
public class Teacher {
private int id;
private String givenName;
private String familyName;
}
I'm creating a service and returning a List of Course objects,
public List<Course> getAll(){
return db.getCourseList();
}
The display is as expected,
[{"id":101,"name":"Introduction to Java","teacher":{"familyName":"Bar","givenName":"Foo","id":201}},{"id":102,"name":"Intermediate Java","teacher":{"familyName":"Prank","givenName":"Foo","id":202}}]
Now I want to customize my JSON object to display in the following format, with only the teacher ID.
[{"id":"100","name":"Introduction to Java","teacherId":"201"},{"id":"101","name":"Intermediate Java","teacherId":"201"}
So this is the view model that I designed.
#XmlRootElement
public class CourseTeacherIdView {
private int id;
private String name;
private int teacherId;
CourseTeacherIdView(){
}
public CourseTeacherIdView(int id, String name, int teacherId){
this.id = id;
this.name = name;
this.teacherId = teacherId;
}
}
And I use this method to return the List of view objects.
public List<CourseTeacherIdView> getAll(){
List<Course> list = db.getCourseList();
List<CourseTeacherIdView> viewList = new ArrayList<>();
for(Iterator<Course> itr = list.iterator(); itr.hasNext();){
Course c = (Course) itr.next();
viewList.add(new CourseTeacherIdView(c.getId(), c.getName(), c.getTeacher().getId()));
}
return viewList;
}
This is the result that I get.
[{},{},{}]
What am I doing wrong.

You can achieve that with Jackson and creating a custom serializer like the following:
public class CourseSerializer extends JsonSerializer<Course> {
#Override
public void serialize(Course value,
JsonGenerator gen,
SerializerProvider serializers) throws IOException {
gen.writeStartObject();
Field[] fields = value.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Object obj = field.get(value);
if (obj instanceof Teacher) {
Teacher teacher = (Teacher) obj;
gen.writeStringField("teacherId", String.valueOf(teacher.getId()));
} else {
gen.writeStringField(field.getName(), obj.toString());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
gen.writeEndObject();
}
}
Test case:
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Course.class, new CourseSerializer());
mapper.registerModule(module);
Teacher teacher1 = new Teacher(123, "teacher1", "surename1");
Teacher teacher2 = new Teacher(234, "teacher2", "surename2");
Course course1 = new Course(1, "course1", teacher1);
Course course2 = new Course(2, "course2", teacher2);
List<Course> courses = Arrays.asList(new Course[]{course1, course2});
String serialized = mapper.writeValueAsString(courses);
}
Output:
[{
"id": "1",
"name": "course1",
"teacherId": "123"
}, {
"id": "2",
"name": "course2",
"teacherId": "234"
}]

If I understood you correctly you could either create new view model representation with only the id and map each object in the list to it, or use #jsonignore on not relevant fields (if jersey is using Jackson). Or even retrieve only the ids from the db. Depends on use case.

Related

How to keep the JSON's order preserved while sending to frontend application from SpringBoot Application's service?

I'm developing a springboot application.
I've a class with the following fields
class MyClass
{
String s1;
String s2;
String s3;
String s4;
//getters setters constructors
}
I'm calling an API which in turn calls a service.
public String myService()
{
JSONArray arr1 = new JSONArray();
for (Items item : itemsList)
{
JSONObject itemObj = new JSONObject();
itemObj.put("s1","Value1");
itemObj.put("s2","Value2");
itemObj.put("s3","Value3")
itemObj.put("s4","Value4");
arr1.put(itemObj);
}
JSONObject out = new JSONObject();
out.put("total_Items", arr1);
return out.toString(); // this is org.json.JSONObject
}
This way I'm able to get the excel with the reordered members as the columns when a button is clicked at the frontend angular app.
What I want is the order of the the members in the columns remains preserved when exporting into an excel sheet.
s1|s2|s3|s4 //as per the above example
I've many other services as well, which return different types of Objects(apart from the MyClass mentioned here) so I wanted to return the elements in the order defined (as per the order of members in the class) from the backend itself.
I know that JSON does not allow us to preserve the order as it internally uses a HASHMAP.
Is there a way to return a JSON response such that the order remains same as that of the class members?
I also tried using GSON in the below way.
public String myService()
{
MyClass itemsArray[] = new MyClass[itemsList.size()];
int i=0;
for (Items item : itemsList)
{
MyClass itemObj = new MyClass();
itemObj.setS1("Value1");
itemObj.setS2("Value2");
itemObj.setS3("Value3")
itemObj.setS4("Value4");
itemsArray[i]=itemObj;
}
Gson gson = new Gson();
return gson.toJson(itemsArray); // this is java.lang.String
}
In this scenario I'm able to get the API response(on POSTMAN) with the elements in ordered fashion but when exporting on the frontend angular app it freezes at the downloading screen.
I tried doing conversion to JSONObject and other things randomly but was not able to make the code work properly.
Is there anyway the problem can be resolved at the backend...or something needs to be done at the frontend only?
Thanks in advance.
If you want to do using DataStructure use LinkedHashMap as given below. It will serialize in inserted order.
public static void main(String args[]){
ObjectMapper mapper = new ObjectMapper();
Map<String, String> itemObj = new LinkedHashMap<>();
itemObj.put("s91","Value1");
itemObj.put("s2","Value2");
itemObj.put("s3","Value3");
itemObj.put("s4","Value4");
try {
String jsonString = mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(itemObj);
System.out.println(jsonString);
}
catch (IOException e) {
e.printStackTrace();
}
}
{
"s91" : "Value1",
"s2" : "Value2",
"s3" : "Value3",
"s4" : "Value4"
}
If you want to serialize in sorted order the use TreeMap, it will serialize in sorted key
Map<String, String> itemObj = new TreeMap<>();
...
{
"s2" : "Value2",
"s3" : "Value3",
"s4" : "Value4",
"s91" : "Value1"
}
This can be done using Jackson library using the #JsonPropertyOrder annotation.
You can define the order of elements as given below above class
#JsonPropertyOrder({ "s3", "s2", "s1", "s4"})
Refer to below a working example
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.istack.NotNull;
import java.io.IOException;
import java.util.UUID;
#JsonPropertyOrder({ "id", "password", "name", "email", "enabled" })
public class UserResource {
private UUID id;
#NotNull
private String name;
#NotNull
private String email;
private boolean enabled;
private String password;
public UserResource(UUID id, String name, String email, boolean enabled, String password) {
this.id = id;
this.name = name;
this.email = email;
this.enabled = enabled;
this.password = password;
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public static void main(String args[]){
ObjectMapper mapper = new ObjectMapper();
try {
UserResource student = new UserResource(UUID.randomUUID(), "sheel", "sheel#c4c.com",true, "$$$$$$%%##^^$DSGHHH");
String jsonString = mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(student);
System.out.println(jsonString);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
and output as given below
{
"id" : "fbfcd21d-731e-4acb-9fec-90a499e47cc9",
"password" : "$$$$$$%%##^^$DSGHHH",
"name" : "sheel",
"email" : "sheel#c4c.com",
"enabled" : true
}

How to write multiple enums data to the single pojo and return it as list

is there a way using java 8 feature to convert multiple eums data to single pojo and return it as list.
I am trying this because i have scenario in application to return thedrop down values from enums from backend.
public enum TransactionTypeStatuses {
NEW("NEW"),
ACTIVE("ACTV"),
String code;
TransactionTypeStatuses(String code) {
this.code = code;
}
public String code() {
return code;
}
}
public enum MemberTypes {
CENTRAL_BANK("MB01"),
DIRECT_PARTICIPANT("MB02");
private String code;
private MemberTypes(String code) {
this.code =code;
}
public String getCode() {
return code;
}
}
public class EnumDataPojo {
private String id;
private String description;
//setters and getters
}
Add the enums data to the pojo and convert the pojo to the json response
public class Application {
public static void main(String[] args) throws JsonProcessingException {
List<EnumDataPojo> response = convertEnumsToPojo(); //contains the data of All enums in "id" and "desscription" format
ObjectMapper mapper = new ObjectMapper();
String response = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(data);
System.out.println(response);
}
stiatic List<EnumDataPojo> convertEnumsToPojo(){
List<EnumDataPojo> dataList = new ArrayList<>();
for(TransactionTypeStatuses enum1: TransactionTypeStatuses.values())
{
EnumDataPojo data1 = new EnumDataPojo ();
data1.setID(enum1.getCode());
data1.setdescription(String.value(enum1));
dataList.add(data1);
}
for(MemberTypes enum2: MemberTypes.values())
{
EnumDataPojo data2 = new EnumDataPojo ();
data2.setID(enum2.getCode());
data2.setdescription(String.value(enum2));
dataList.add(data2);
}
return dataList;
}
}
json response from the above code
{
"transactionTypeStatuses":[ //enum
{
"id":"NEW",
"description":"New"
},
{
"id":"ACTV",
"description":"active"
}
],
"memberTypes":[ //enum
{ "id": "MB01", "description": "Central Bank" },
{ "id": "MB02", "description": "Direct Participant" }
]
}
Updated answer
public class EnumDataValues{
private String id;
private String description;
//getters and setters
}
//convert each enum to pojo and put it in list.
private List<EnumDataValues> getEnumReferenceData(Class<?> c) {
List<EnumDataValues> referenceDataList = new ArrayList<>();
List enumConstants = Arrays.asList(c.getEnumConstants());
if (!enumConstants.isEmpty()) {
enumConstants.forEach(t -> {
EnumDataValues referenceData = new EnumDataValues();
referenceData.setId(String.valueOf(String.valueOf(t)));
referenceData.setDescription(String.valueOf(t));
referenceDataList.add(referenceData);
});
}
return referenceDataList;
}
I don't think your code will give you correct response. You should add data in loop to get all values of each enums
List<EnumDataValues> enum1DataList = new ArrayList<>();
for(Enum1 enum1: enum1.values()){
EnumDataValues data1 = new EnumDataValues ();
data1.setID(enum1.getCode());
data1.setdescription(String.value(enum1));
dataList.add(data1);
}
Do same for other enums
Update:
Use Map<String, List<EnumDataValues>> as response type and set the enum data.
Map<String, List<EnumDataValues>> convertEnumsToPojo(){
List<EnumDataPojo> dataList = new ArrayList<>();
for(TransactionTypeStatuses enum1: TransactionTypeStatuses.values())
{
EnumDataPojo data1 = new EnumDataPojo ();
data1.setID(enum1.getCode());
data1.setdescription(String.value(enum1));
dataList.add(data1);
}
List<EnumDataPojo> dataList2 = new ArrayList<>();
for(MemberTypes enum2: MemberTypes.values())
{
EnumDataPojo data2 = new EnumDataPojo ();
data2.setID(enum2.getCode());
data2.setdescription(String.value(enum2));
dataList2.add(data2);
}
Map<String, List<EnumDataValues>> map = new HashMap<>();
map.put("transactionTypeStatuses", enum1DataList);
map.put("memberTypes", enum2DataList);
return map;
}

#OneToMany getter returns sometimes objects and sometimes ids (in the array)

I've a #OneToMany relation from School to teachers.
If I load the teachers on frontend I get a list with teachers; but some of them are instead of objects ids....
#OneToMany
#JsonManagedReference(value = "teachers")
#JsonIgnore // only admins
private Set<Teacher> teachers = new HashSet<>();
It's #JsonIgnore, because I made a extra call in my controller to prevent to long loading times.
Next I don't delete the entries in the db - I just disable them; here is the getter with the filter:
#JsonIgnore // only admin
public Set<Teacher> getTeachers() {
return teachers.stream().filter(DbModel::isEnabled).collect(Collectors.toSet());
}
Finally the access in the controller:
#RequestMapping(value = "/{schoolId}/teacher", method = RequestMethod.GET)
public Set<Teacher> readTeacher(#PathVariable long schoolId, #RequestParam("teacher") long adminId) {
School school = schoolRepository.findOne(schoolId);
Teacher admin = teacherRepository.findOne(adminId);
if (school == null) {
throw new NotFoundException(School.class, schoolId);
}
if (admin == null) {
throw new NotFoundException(Teacher.class, adminId);
}
if (!admin.isAdmin(school)) {
throw new NoAccessRightException();
}
return school.getTeachers();
}
How to get always only objects?
Update: Example output:
[
{
"#id": 1,
"id": 1,
// contend
},
{
"#id": 5,
"id": 2,
// contend
},
2,
{
"#id": 6,
"id": 8,
// contend
},
4,
3
]
another question: can I make a projection on only one call?
To ignore try this.
#JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class MyClass {
#JsonIgnore // only admins
private Set<Teacher> teachers = new HashSet<>();
}
So Jackson will include all properties, except those marked with #JsonIgnore.
To add only the ID of the children, you need a custom serializer.
#JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class School {
#JsonSerialize(using = TeachersSerializer .class)
private Set<Teacher> teachers = new HashSet<>();
}
The serializer:
public class TeachersSerializer extends JsonSerializer<Set<Teacher>> {
#Override
public void serialize(Set<Teacher> values, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartArray();
for (Teacher value : values) {
jgen.writeStartObject();
jgen.writeNumberField("id", value.getId());
jgen.writeEndObject();
}
jgen.writeEndArray();
}
}
If you want to return only in this controller, create a Teachers class.
#JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Teachers implements Serializable {
#JsonSerialize(using = TeachersSerializer.class)
private Set<Teacher> teachers = new HashSet<>();
public Teachers(Set<Teacher> teachers){
this.teachers = teachers;
}
}
In your controller:
public class Controller {
public Teachers readTeacher(...){
....
return new Teachers(school.getTeachers());
}
}

Passing parameters to custom Deserializer

I have json that contains multiple rows.
{
"rows": [{
"aId": 408,
"aFrstNm": "TIM",
},
{
"aId": 410,
"aFrstNm": "BOB",
},
{
"aId": 409,
"aFrstNm": "HENRY",
}]
}
and POJOs
public class User extends Employee {
#JsonProperty("tableName")
private Double aID;
#JsonProperty("aFrstNm")
private String aFrstNm;
getters and setters ommitted
}
public class Employee {
}
public class Employees {
private Collection<? extends Employee> rows;
getters and setters ommitted
}
What I am trying to do is to pass the User class to a custom deserializer to be able return employees as a collection of Users that extend Employee:
SimpleModule simpleModule = new SimpleModule("MyModule")
.addDeserializer(Employees.class, new JsonDeserializer<Employees>() {
#Override
public Employees deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectMapper mapper = (ObjectMapper) p.getCodec();
final Class<?> clazz = (Class<?>) ctxt.getAttribute("mappingClass");
final JsonNode jsonNode = (JsonNode) mapper.readTree(p).get("rows");
Employees employees = new Employees();
Collection<Product> rows = new ArrayList();
if (jsonNode.isArray()) for (final JsonNode objNode : jsonNode) {
boolean isValueNode = objNode.isValueNode();
String json = mapper.writeValueAsString(objNode);
Product product = (Product) new ObjectMapper().readValue(json, clazz);
rows.add(product);
System.out.println(jsonNode);
}
employees.setRows(rows);
return employees;
}
});
mapper.registerModule(simpleModule);
the variable clazz (final Class clazz) attribute should contain the class a parameter. I so far have been unable to find a mechanism that allows this. DeserializationContext seems to be promising but I have not been able to make this work. Any suggestions on using DeserializationContext or some other way to pass parameters?
Tnx.

Using Jackon ObjectMapper to map to array of classes, alongside other fields?

I have some Json in the following form:
"items": [
{
"id": 1,
"text": "As a user without a subscription, I get a choice of available ones.",
"status": "finished",
"tags": [
{
"id": 1234,
"name": "feature=subs"
},
{
"id": 1235,
"name": "epic=premium"
}
]
},
{
"id": 2,
...
There are more fields but I have ommitted them for clarity.
I am trying to map each story to a Story Class with fields ID, Text, Status and a list of Tags. I've got it working fine using the following:
public Project JsonToProject(byte[] json) throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode rootNode = mapper.readValue(json, JsonNode.class);
int storyCount = rootNode.get("totalItems").asInt();
ArrayNode itemsNode = (ArrayNode) rootNode.get("items");
Project project = new Project();
for (int i = 0; i < storyCount; i++)
{
Story story = JsonToStory(rootNode.get(i));
project.addStory(story);
}
return project;
}
Where a project is simple an ArrayList of Stories, and JsonToStory is the following method:
public Story JsonToStory(JsonNode rootNode) throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Story story = mapper.readValue(rootNode, Story.class);
return story;
}
The Story Class is as follows:
public class Story {
private int id;
private String text = new String();
private String status = new String();
private final List<Tag> tags = new ArrayList<Tag>();
public void setId(int i)
{
id = i;
}
public void setText(String s)
{
text = s;
}
public void setStatus(String s)
{
status = s;
}
public void setTags(Tag[])
{
???
}
}
with the get methods and print methods. The Tag Class simply contains two string fields.
I don't know how to structure the setTags method, in order to result in an arraylist of Tag objects, and haven't been able to find anything to help.
Thanks!
You have marked your tags as final, which will probably block the setter from setting the tags. You can try this:
public class Story {
private int id;
private String text = new String();
private String status = new String();
private List<Tag> tags;
public void setTags(List<Tag> tags){
this.tags = tags;
}
OR
public class Story {
private int id;
private String text = new String();
private String status = new String();
private Tag[] tags;
public void setTags(Tag[] tags){
this.tags = tags;
}

Categories

Resources