Generating JSON from POJO for a specific scenario - java

I have used Jackson and JSONObject to generate a plain JSON - things are fine here. I have a specific case where my pojo looks like below and i need the JSON is the specified format.
package test;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "login")
public class LoginApi implements IRestBean {
private String username;
private String password;
private String sfSessionId;
private String sfServerUrl;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSfSessionId() {
return sfSessionId;
}
public void setSfSessionId(String sfSessionId) {
this.sfSessionId = sfSessionId;
}
public String getSfServerUrl() {
return sfServerUrl;
}
public void setSfServerUrl(String sfServerUrl) {
this.sfServerUrl = sfServerUrl;
}
}
The JSON that i am able to generate looks like this:
{
"username" : null,
"password" : null,
"sfSessionId" : null,
"sfServerUrl" : null
}
But this is not my requirement - i need the JSON in the below format so that my server accepts this as a valid JSON:
{
"#type":"login",
"username":"username#domain.com",
"password":"password",
"sfSessionId":null,
"sfServerUrl":null
}
Please help. Thanks in advance!

Add a private field to the POJO with the type.
#XmlRootElement(name = "login")
public class LoginApi implements IRestBean {
...
#XmlAttribute(name = "type")
private String getJsonType() {
return "login";
}
...
}
Note the use of XmlAttribute to automatically append an "#" to the name.

Change the IRestBean interface to include the #JsonTypeInfo annotation:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#type")
public interface IRestBean {
...
}
Next, annotate the LoginApi class with #JsonTypeName:
#XmlRootElement(name = "login")
#JsonTypeName("login")
public class LoginApi implements IRestBean {
...
}
These are both Jackson-specific annotations.

Related

Polymorphic deserialization in jackson sets value null for specific fields

Problem statement
Is that the JSON which is to be deserialize into the below given POJO's
is setting value of credentialType as null when i send the below JSON through postman
{
"credential": [
{
"#type": "mobile",
"credentialName": "cred-2",
"email": "s#s.com"
},
{
"#type": "card",
"credentialNumber": "1"
}
]
}
Expected outcome
What i want to achieve is that with the above JSON the credential type should be set as either MOBILE for MobileCredentialDto or CARD for CardCredentialDto
#Getter
public class SecureDto {
private List<CredentialDto> credential;
#JsonCreator
public HandoutDto(#JsonProperty("credential") final List<CredentialDto> credential) {
this.credential = Collections.unmodifiableList(credential);
}
}
#Getter
public class SecureDto {
private List<CredentialDto> credential;
#JsonCreator
public HandoutDto(#JsonProperty("credential") final List<CredentialDto> credential) {
this.credential = Collections.unmodifiableList(credential);
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
#JsonSubTypes({
#JsonSubTypes.Type(value = CardCredentialDto.class, name = "card"),
#JsonSubTypes.Type(value = MobileCredentialDto.class, name = "mobile"),
})
#Getter
#Setter
public class CredentialDto {
private CredentialType credentialType;
#JsonCreator
public CredentialDto(#JsonProperty("credentialType") final String credentialType) {
this.credentialType = CredentialType.valueOf(credentialType);
}
public CredentialDto() {
}
public void setCredentialType(final CredentialType credentialType) {
this.credentialType = CredentialType.MOBILE;
}
}
#Getter
#Setter
public class MobileCredentialDto extends CredentialDto {
private String credentialName;
private String email;
public MobileCredentialDto(final String credentialId,
final String state,
final String credentialNumber,
final String credentialName,
final String email) {
super(credentialId, state, credentialNumber, CredentialType.MOBILE.name());
this.credentialName = credentialName;
this.email = email;
}
public MobileCredentialDto() {
}
public String getCredentialName() {
return credentialName;
}
public String getEmail() {
return email;
}
}
#Getter
#Setter
public class CardCredentialDto extends CredentialDto {
public CardCredentialDto(final String credentialId,
final String state,
final String credentialNumber) {
super(credentialId, state, credentialNumber,CredentialType.CARD.name());
}
public CardCredentialDto() {
}
}
public enum CredentialType {
MOBILE("MOBILE"),
CARD("CARD");
private final String name;
CredentialType(final String name) {
this.name = name;
}
}
I Found the answer.
what i did was set the visible = true in JsonTypeInfo.
In short by setting the visible = true allowed the jackson to do the following
Note on visibility of type identifier: by default, deserialization (use during reading of JSON) of type identifier is completely handled by Jackson, and is not passed to deserializers. However, if so desired, it is possible to define property visible = true in which case property will be passed as-is to deserializers (and set via setter or field) on deserialization.
Refer the doc here for more understanding.
https://fasterxml.github.io/jackson-annotations/javadoc/2.4/com/fasterxml/jackson/annotation/JsonTypeInfo.html
Below is the code
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME ,
visible = true,
property = "credentialType")
#JsonSubTypes({
#JsonSubTypes.Type(value = CardCredentialDto.class, name = "card"),
#JsonSubTypes.Type(value = MobileCredentialDto.class, name = "mobile"),
})
#Getter
#Setter
public class CredentialDto {
#JsonProperty(value = "credentialType")
private CredentialType credentialType;
#JsonCreator
public CredentialDto(#JsonProperty("credentialType") final String credentialType) {
this.credentialType = CredentialType.valueOf(credentialType);
}
public CredentialDto() {
}
public void setCredentialType(final CredentialType credentialType) {
this.credentialType = CredentialType.MOBILE;
}
}
and the json will look like this
{
"credential": [
{
"credentialType": "mobile",
"credentialName": "cred-2",
"email": "s#s.com"
},
{
"credentialType": "card",
"credentialNumber": "1"
}
]
}

sentence case json property is not mapping with pojo class in rest api

i am making a post request to rest api from postman with body as json :
{
"username":"ramakanta",
"password":"test",
"StudentID":"1025"
}
And server side resouces are
public class PushDataEntity {
private String username;
private String password;
private String StudentID;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getStudentID() {
return StudentID;
}
#JsonProperty("StudentID")
public void setStudentID(String StudentID) {
this.StudentID = StudentID;
}
}
#Path("/pushdata")
public class PushDataServiceImpl {
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response dataPush(#Context HttpServletRequest request,PushDataEntity pushDataEntity) throws Exception {
System.out.println(pushDataEntity.getStudentID());//null
}
username and password is mapping but StudentID is not mapping to pojo property. I have tried adding #JsonProperty("StudentID") to setter , getter and property level even , didn't workout. i dont want any change in json, what i should change in java side to make that json property mapped to pojo property. please provide a solution for this. Thanks in advance.
You are sending StudentId, but having StudentID in POJO (the last "d" is different). Either change the JSON in request, or add #JsonProperty("StudentId") annotation to the POJO.
UPDATE
Signature of the method is wrong, there is no such annotation: #ContextHttpServletRequestrequest, it should be
public Response dataPush(#Context HttpServletRequest request,,PushDataEntity pushDataEntity) throws Exception {
You need to put #JsonProperty over the getter function of StudentID.

Jackson serialization with null

Jackson annotations are for serialization but I cannot find the solution to have two different views with different behavior. Having the following json:
{
"username": "username",
"manager": null,
"password": "pwd"
}
I'd like to have to following output in case the first view (public: no null and no sensitive information):
{
"username": "username"
}
For the second view (internal: incoming nulls are showed sensitive information is hashed):
{
"username": "username",
"manager": null,
"password": "hashedValue"
}
The problem is when the optional fields are not provided like in the following json:
{
"password": "pwd"
}
I'd like to have an empty for public and only the hashed password in the internal view:
{
"password": "hashedValue"
}
I have the following class:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Test {
private String username;
private String manager;
private String password;
#JsonView(Views.Internal.class)
#JsonSerialize(using = Md5Serializer.class)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getManager() {
return manager;
}
public void setManager(String manager) {
this.manager = manager;
}
}
This can do most what I'd like to have but it cannot make difference if a value is null because of the default value or because it's coming from the JSON.
An other approach was to use a Map inside the object:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Test {
private Map<String, String> content = new HashMap<>();
#JsonView(Views.Internal.class)
#JsonSerialize(using = Md5Serializer.class)
#JsonIgnore
public String getPassword() {
return this.content.get("password");
}
#JsonSetter
public void setPassword(String password) {
this.content.put("password", password);
}
#JsonIgnore
public String getUsername() {
return this.content.get("username");
}
#JsonSetter
public void setUsername(String username) {
this.content.put("username", username);
}
#JsonIgnore
public String getManager() {
return this.content.get("manager");
}
#JsonSetter
public void setManager(String manager) {
this.content.put("manager", manager);
}
#JsonAnyGetter
public Map<String, String> getContent() {
return this.content;
}
}
In this case the problem that the password is not hashed (and the hashed value cannot be stored here).
If the getters are used and the JsonAnyGetter is removed then the incoming null cannot be serialized as the JsonInclude ignores the field. I was reading a lot that a BeanSerializerModifier can be used but that is also needed to be register in all ObjectMapper instance.
Also I'm not sure that without setting the view I'd like to have only the Public view but currently all non-null values are shown. Any idea how it can be achieved only by annotations?

Mapping JSON into POJO using Gson

I have the following JSON to represent the server response for a salt request:
{
"USER":
{
"E_MAIL":"email",
"SALT":"salt"
},
"CODE":"010"
}
And i tried to map it with the following POJO:
public class SaltPOJO {
private String code = null;
private User user = null;
#Override
public String toString() {
return this.user.toString();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public class User {
private String e_mail = null;
private String salt = null;
#Override
public String toString() {
return this.e_mail + ": " + this.salt;
}
public String getE_mail() {
return e_mail;
}
public void setE_mail(String e_mail) {
this.e_mail = e_mail;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
}
Now everytime i do this:
Gson gson = new Gson();
SaltPOJO saltPojo = gson.fromJson(json.toString(), SaltPOJO.class);
Log.v("Bla", saltPojo.toString());
The saltPojo.toString() is null. How can i map my JSON into POJO using Gson?
Is the order of my variables important for the Gson mapping?
Is the order of my variables important for the Gson mapping?
No, that's not the case.
How can i map my JSON into POJO using Gson?
It's Case Sensitive and the keys in JSON string should be same as variable names used in POJO class.
You can use #SerializedName annotation to use any variable name as your like.
Sample code:
class SaltPOJO {
#SerializedName("CODE")
private String code = null;
#SerializedName("USER")
private User user = null;
...
class User {
#SerializedName("E_MAIL")
private String e_mail = null;
#SerializedName("SALT")
private String salt = null;
You don't have proper mapping between your getter and setter. If you change your json to something like below, it would work:
{
"user":
{
"email":"email",
"salt":"salt"
},
"code":"010"
}
If you are getting json form third party then unfortunately, you would have to change your pojo or you could use adapter.

Jackson JSON Processor: SerializationConfig.Feature.USE_ANNOTATIONS setted to false

Hi I'm facing with a nasty problem while using Jackson JSON PRocessor with ObjectMapper class.
This is my test class that should Serialize an Object (UserHeader) into a Json String.
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
public class TestJson {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
UserHeader userHeader = new UserHeader();
userHeader.setFirstName("A");
userHeader.setLastName("A1");
userHeader.setSystem("2A");
mapper.configure(SerializationConfig.Feature.USE_ANNOTATIONS, false);
StringWriter sw = new StringWriter();
mapper.writeValue(sw, userHeader);
System.out.println(sw.toString());
}
}
This is my UserHeader class with Annotations that are used from a different ObjectMapper (not this one)
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.annotate.JsonSerialize.Typing;
#JsonSerialize(typing=Typing.STATIC)
public class UserHeader implements Serializable,LoggedObject, MessagesInterface {
private static final long serialVersionUID = 1L;
private String system;
private String userName;
private String firstName;
private String lastName;
private List<Scrivania> scrivanie;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
#JsonProperty("Nome utente")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#JsonProperty("Cognome utente")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#JsonProperty("Scrivanie associate")
public List<Scrivania> getScrivanie() {
return scrivanie;
}
public void setScrivanie(List<Scrivania> scrivanie) {
this.scrivanie = scrivanie;
}
#JsonProperty("Sistema (IAM o EDOC)")
public String getSystem() {
return system;
}
public void setSystem(String system) {
this.system = system;
}
#Override
#JsonIgnore
public String getObjectId() {
return this.userName;
}
#Override
#JsonIgnore
public Object getObjectData() {
try {
return this.clone();
}
catch (Exception e) {
return null;
}
}
#Override
public String getName() {
return this.userName;
}
}
However if I run the main method the system returns to me this exception:
Exception in thread "main" java.lang.NullPointerException
at org.codehaus.jackson.map.introspect.AnnotatedClass.resolveClassAnnotations(AnnotatedClass.java:295)
at org.codehaus.jackson.map.introspect.AnnotatedClass.construct(AnnotatedClass.java:141)
at org.codehaus.jackson.map.introspect.BasicClassIntrospector.forClassAnnotations(BasicClassIntrospector.java:185)
at org.codehaus.jackson.map.introspect.BasicClassIntrospector.forClassAnnotations(BasicClassIntrospector.java:15)
at org.codehaus.jackson.map.SerializationConfig.introspectClassAnnotations(SerializationConfig.java:661)
at org.codehaus.jackson.map.ser.BasicSerializerFactory.createTypeSerializer(BasicSerializerFactory.java:180)
at org.codehaus.jackson.map.ser.BeanSerializerFactory.findPropertyContentTypeSerializer(BeanSerializerFactory.java:406)
at org.codehaus.jackson.map.ser.BeanSerializerFactory._constructWriter(BeanSerializerFactory.java:778)
at org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(BeanSerializerFactory.java:608)
at org.codehaus.jackson.map.ser.BeanSerializerFactory.constructBeanSerializer(BeanSerializerFactory.java:436)
at org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanSerializer(BeanSerializerFactory.java:349)
at org.codehaus.jackson.map.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:295)
at org.codehaus.jackson.map.ser.StdSerializerProvider._createUntypedSerializer(StdSerializerProvider.java:778)
at org.codehaus.jackson.map.ser.StdSerializerProvider._createAndCacheUntypedSerializer(StdSerializerProvider.java:731)
at org.codehaus.jackson.map.ser.StdSerializerProvider.findValueSerializer(StdSerializerProvider.java:369)
at http://stackoverflow.com/questions/6661717/how-to-process-an-invalid-value-with-jackson-json-processororg.codehaus.jackson.map.ser.StdSerializerProvider.findTypedValueSerializer(StdSerializerProvider.java:452)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:597)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:280)
at org.codehaus.jackson.map.ObjectMapper._configAndWriteValue(ObjectMapper.java:2260)
at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1813)
at it.unina.edoc.json.TestJson.main(TestJson.java:65)
I have no idea about this exception because #Annotations should be ignored due to USE_ANNOTATION false config.
Moreover if I set USE_ANNOTATION to true the error disappears.
I have these jars on my buildpath:
jackson-core-asl-1.8.3.jar
jackson-jaxrs-1.8.3.jar
jackson-mapper-asl-1.8.3.jar
jackson-xc-1.8.3.jar
The usage of the DeserializationConfig.Feature.USE_ANNOTATIONS property (set to false) will cause the JACKSON DeserializerConfig class to use a NopAnnotationIntrospector. Annotations of a class will then be resolved using this NopAnnotationIntrospector. The NopAnnotationIntrospector will return false on each isHandled request for any annotation on a class - and in fact will not use this annotation in further processing.
So - the system still "inspects" the annotations - which have to be on the Classpath in this case. As Android does not provide any jaxb-api annotations this leads to the NoClassDefFoundError.
I expected USE_ANNOTATIONS = false would bring JACKSON to totally ignore any annotations - but unfortunately it does not. I will now use the Jackson Streaming API to parse the JSON string instead of using JACKSON Data Binding capabilities.

Categories