Json : generate object id instead of real object - json

On my server I have complex objects but in my web client I would like to get a simple representation of these objects.
I would like to replace object with their id for example:
#JsonAutoDetect
public class Event{
private Long id;
private String name;
private User user;
#JsonProperty("event_id")
public Long getId() {
return event_id;
}
public String getName() {
return name;
}
public String getUser() {
return user;
}
....setters
}
When generating Json I will get Event class and user class inside
Is there anyway to replace the User with user id?
Since I have many complex objects I am looking for an elegant way like maybe an annotation over the User object that will return only the object id.
Ofcourse I will need the other way around. getting the id and transforming it to the object.
BTW: I'm using Jackson
Any Ideas?
10x
Noam

You can expose a property for Id and ignore the sub-entity property like this:
class Event {
private User user;
public Long getUserId() {
return user.getId();
}
public void setUserId(Long userId) {
this.user = retrieveUserById(userId); // or any other code
}
#JsonIgnore
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

Here is my idea how to achieve it. You can use #JsonRawValue(true) annotation on your user property. This will make Jackson treat user object as already serialized stuff. And then just override .toString() method for User class to make it return it's id as a string.
So in your Event class:
public class Event {
...
#JsonRawValue(true)
private User user;
...
}
And in your User class:
public class User {
...
private Integer id;
...
#Override
public String toString() {
return id.toString();
}
...
}
This will result in e.g. "user": 5 in your json (no double quotes will be added around User object serizlized value).

Related

Spring RestTemplate response body is null when trying to parse JSON

I've been trying many solutions from similar solved problems in this page but I can't make it work. I'm making a get petition to obtain an array JSON, and I want to map those values into my class.
I got this class:
public class Devices {
private String DeviceName;
private String DeviceDescription;
public String getDeviceName() {
return DeviceName;
}
public void setDeviceName(String deviceName) {
this.DeviceName = deviceName;
}
public String getDeviceDescription() {
return DeviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.DeviceDescription = deviceDescription;
}
}
The GET petition returns this JSON below:
[{"DeviceName":"AMIXT-20EC-VIDM0000","DeviceDescription":"Samsung device "},{"DeviceName":"AMIXT-E0F9-VIDM0001","DeviceDescription":"Tablet Huawei"}]
I've tried solutions like this one (also tried with getForObject):
ResponseEntity<Devices[]> responseEntity = restTemplate.getForEntity(url, Devices[].class);
As we can see in this photo, the body properties are null
What am I missing?
The issue is related to the name of the fields in the JSON, name start with an upper-case letter.
One simple solution would be to use #JsonProperty annotation on the variables defined in the Devices class
public class Devices {
#JsonProperty("DeviceName")
private String deviceName;
#JsonProperty("DeviceDescription")
private String deviceDescription;
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
}

How to combine #JsonView with #JsonProperty?

I have a DTO class that should serve json via a spring-mvc #RestController.
I want to provide different version/views on the same object. Especially, there are fields that are only used in VERSION_1 of the api, and some only in VERSION_2.
Problem: I could add #JsonView for this, but my goal is also to rename those fields. Some fields should actually replace the same name from previous versions.
Example:
public class Person {
#JsonView(View.Version_1.class)
#JsonProperty("name")
private String name; //eg only the firstname
#JsonView(View.Version_2.class)
#JsonProperty("name")
private NameDTO namedto; //now changing to first+last name
static class NameDTO {
private String firstname;
private String lastname;
}
}
#RestController
public class MyServlet {
#GetMapping("/person/{id}")
#JsonView(View.Version_1.class)
public PersonDTO person1(int id) {
//...
}
#GetMapping("/person_new/{id}")
#JsonView(View.Version_2.class)
public PersonDTO person2(int id) {
//...
}
}
So, depending on the view/version, you would get the same json field firstname, but with different content.
In this example, using V1 would give:
{"name": "john"}
Whereas using V2 should result in:
{"name": {"firstname": "john", "lastname": "doe"}}
BUT not with he code above, as jackson complains:
com.fasterxml.jackson.databind.JsonMappingException: Conflicting
getter definitions for property "name".
Is that possible at all?
I found a way using:
https://github.com/jonpeterson/spring-webmvc-model-versioning
Basic idea is to add a custom VersionedModelConverter that is applied on #VersionedModelConverter annotated webservice response classes.
#Configuration
#Import(VersionedModelResponseBodyAdvice.class)
public class SpringMvcVersioningConfiguration {
//register in jackson. spring-boot automatically registers any module beans
#Bean
public Model versioningModel() {
return new VersioningModule();
}
}
#GetMapping
#VersionedResponseBody(defaultVersion = "2.0")
public Person person() {
}
#JsonVersionedModel(currentVersion = "3.0" toPastConverterClass = PersonConverter.class)
public class Person {
}
public class PersonConverter implements VersionedModelConverter {
#Override
public ObjectNode convert(ObjectNode modelData, String modelVersion, String targetModelVersion, JsonNodeFactory nodeFactory) {
Double modelv = Double.valueOf(modelVersion);
Double targetv = Double.valueOf(targetVersion);
//todo if-else based on model version
Object node = modelData.remove("fieldname");
//node.change...
modelData.set("fieldname_renamed", node);
}
}

DAO MVC: why my class is NOT POJO

I use DAO MVC, and I after some googling I consider to store some variables as Enum in java and String in MySQL. So I create in Item.java (that will be persist into Item table) static initialization and static methods to convert Enum into String and vise versa.
But someone said me that after this static initialization and static methods my Item.java class became NOT POJO.
Question:
Why it became NOT POJO?
And if I'll make those methods not static Item.java class will be POJO?
EDITED: MY code:
package model;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class Order {
public enum OrderStatus {
NOT_REVIEWED,
APPROVED,
REJECTED,
RETURNED
}
// ==================
// = Transient =
// ==================
private static final Map<String, OrderStatus> convertStringToOrderStatusMap = new HashMap<String, OrderStatus>(3);
private static final Map<OrderStatus, String> convertOrderStatusToStringMap = new EnumMap<OrderStatus, String>(OrderStatus.class);
static {
convertStringToOrderStatusMap.put("not reviewed", OrderStatus.NOT_REVIEWED);
convertStringToOrderStatusMap.put("approved", OrderStatus.APPROVED);
convertStringToOrderStatusMap.put("rejected", OrderStatus.REJECTED);
convertStringToOrderStatusMap.put("returned", OrderStatus.RETURNED);
convertOrderStatusToStringMap.put(OrderStatus.NOT_REVIEWED, "not reviewed");
convertOrderStatusToStringMap.put(OrderStatus.APPROVED, "approved");
convertOrderStatusToStringMap.put(OrderStatus.REJECTED, "rejected");
convertOrderStatusToStringMap.put(OrderStatus.RETURNED, "returned");
}
// ==================
// = Attributes =
// ==================
private Integer orderId; //Primary key
private OrderStatus status;
private Integer reimbursement;
private String firstName;
private String secondName;
private String passportData;
private String pickUpDate;
private String dropOffDate;
//java.util.Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2011-05-18 16:29:31");
private String customerCell;
private String customerAddress;
// ==================
// = Foreign Keys =
// ==================
private User user;
private Car car;
// ==================
// = Public methods =
// ==================
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getStatus() {
return convertOrderStatusToString(status);
}
public void setStatus(OrderStatus status) {
this.status = status;
}
public Integer getReimbursement() {
return this.reimbursement;
}
public void setReimbursement(Integer value) {
this.reimbursement = value;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSecondName() {
return secondName;
}
public void setSecondName(String secondName) {
this.secondName = secondName;
}
public String getPassportData() {
return passportData;
}
public void setPassportData(String passportData) {
this.passportData = passportData;
}
public String getPickUpDate() {
return pickUpDate;
}
public void setPickUpDate(String pickUpDate) {
this.pickUpDate = pickUpDate;
}
public String getDropOffDate() {
return dropOffDate;
}
public void setDropOffDate(String dropOffDate) {
this.dropOffDate = dropOffDate;
}
public String getCustomerCell() {
return customerCell;
}
public void setCustomerCell(String customerCell) {
this.customerCell = customerCell;
}
public String getCustomerAddress() {
return customerAddress;
}
public void setCustomerAddress(String customerAddress) {
this.customerAddress = customerAddress;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public static OrderStatus converStringToOrderStatus(String status) {
return convertStringToOrderStatusMap.get(status);
}
public static String convertOrderStatusToString(OrderStatus status) {
return convertOrderStatusToStringMap.get(status);
}
}
Because a Plain Old Java Object only has data. Adding logic and methods means that it's no longer Plain Old Java Object.
That doesn't necessarily make it a bad thing, but you might be able to refactor the logic out into a class of it's own.
Lets ignore POJO.
What they mean is Service Oriented vs Domain Driven.
Service Oriented follows strict separation of behavior from state. They call POJOs data objects which are essentially glorified structs. Thus you would put the static methods in the Service. In fact you probably wouldn't even want the methods static as that is also against the service oriented approach (see dependency injection and evil singleton).
Domain Driven follows the idea of classic OOP (e.g. Rails Active Record) in which they do believe its OK to put behavior in their POJOs. Consequently because state + behavior are coupled there is only one implementation and thus static methods in the domain object are OK.
If your going the DAO route your most likely doing Service Oriented. My opinion is if your going to do the DAO POJO route you should use immutable objects (shameless plug) for those data objects.
Finally putting an inline enum into a class from my knowledge does not violate any definition of POJO. That being said you should know about #Enumerated since your using JPA.

Modify JSon responce for #ManyToOne association using Jackson and Spring

I have two classes:
public class Team {
private Long id;
private String name;
...
}
public class Event {
private Long id;
#ManyToOne
private Team homeTeam;
#ManyToOne
private Team guestTeam;
...
}
Controller:
public #ResponseBody List<Event> getAll() {...
}
Now I have Json:
[{"id":1,"homeTeam":{"id":2,"name":"Golden State"},"guestTeam":{"id":1,"name":"Philadelphia"},...
What I want:
[{"id":1,"homeTeam":"Golden State","guestTeam":"Philadelphia",...
How I can point Jackson to output only name of Team instead of full Object?
Benoit's answer will not generate JSON of the desired form, it would produce something like this:
[{"id":1,"homeTeam":{"name":"Golden State"},"guestTeam":{"name":"Philadelphia"},...
Instead what you want to do is make your Team class look something like this:
public class Team {
private Long id;
private String name;
public Long getId() {
return id;
}
#JsonValue
public String getName() {
return name;
}
...
}
This will produce the desired JSON:
[{"id":1,"homeTeam":"Golden State","guestTeam":"Philadelphia",...
But may require additional handling for deserialization.
Exclude all properties of the Team object except the name using : #JsonIgnoreProperties
#JsonIgnoreProperties
public String getPropertyToExclude() {
return propertyToExclude;
}
So that Jackson will serialize only the Team name in the JSON.

Silverlight 4 DataContractJsonSerializer, private fields of a derived class

I use DataContractJsonSerializer to deserialize json data in Silverlight 4.
Json data key names do not match my class property names; so I guess I have to use
DataMemberAttribute. So I did the following:
[DataContract]
public class Person : Model
{
[DataMember(Name = "id")]
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
[DataMember(Name = "name")]
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Now deserialization fails because I didn't apply DataContractAttribute to Person's base class Model. Is it a strict requirement? Also, after I applied DataContractAttribute to Model, deserialization fails again, because I applied DataMember attributes to private fields, not to the public properties. Why can't I apply them to private members (the documentation seems to say otherwise).
NOTE: server-side code is not ASP.NET; so WCF isn't used.
In order to get the private members to serialize over WCF correctly, we had to change them all to protected internal instead of private. Maybe the same applies for DataContractJsonSerializer?