Jackson library: custom mapping of a Java object in a JSON element - java

I have two class
public class Person {
private long id;
private String name;
private Gender gender;
// getter and setter omitted
}
and
public class Gender {
private long id;
private String value;
// getter and setter omitted
}
By default the JSON mapping with Jackson library of a Person object is:
{
id: 11,
name: "myname",
gender: {
id: 2,
value: "F"
}
}
I'd like to known how to configure Jackson to obtain:
{
id: 11,
name: "myname",
gender: "F"
}
I don't want mapping all the Gender object but only its value property.

You can use a custom serializer:
public class GenderSerializer extends JsonSerializer<Gender> {
public GenderSerializer() {
}
#Override
public void serialize(Gender gender, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(gender.getValue());
}
}
And in your Person class:
public class Person {
private long id;
private String name;
#JsonSerialize(using = GenderSerializer.class)
private Gender gender;
}

you might wanna see this for custom mapping OR if you need a short cut then you can change getter/setter of gender like this
public String getGender(String type){
this.getGender().getValue();
}
public void setGender(String value){
Gender gender = new Gender();
gender.setId(2);
gender.setValue(value);
this.gender = gender;
}
further you can also put condition for setting male/female on value= M/F

No need for custom serializer/deserializer if you can modify Gender class. For serialization you can use #JsonValue, for deserialization simple constructor (optionally annotated with #JsonCreator):
public class Gender {
#JsonCreator // optional
public Gender(String desc) {
// handle detection of "M" or "F"
}
#JsonValue
public String asString() { // name doesn't matter
if (...) return "M";
return "F";
}
}
Alternatively you could use a static factory method instead of constructor; if so, it must be annotated with #JsonCreator.
And return type of method annotated with #JsonValue can be anything that Jackson knows how to serialize; here we use String, but Enum, POJO and such are fine as well.

Related

Spring boot #JsonProperty to map nested object through parent.child path

I have the following Data structure coming to my server through an Http call
person {
name: "xyz",
age: 30,
status: {
cd: "M",
desc: "Married"
}
}
Then I have the following entity class
#Setter
#Getter
public class Person {
private String name;
private int age;
#JsonProperty("status.cd")
private String status_cd;
#JsonProperty("status/desc")
private String status_desc;
}
Either way I try I'm not able to get spring boot to match the json property text (these two values are always null)... is that doable & if so, how?
the final JSON I need to be produced is the following
{
name: "XYZ",
age: 30,
status_cd: "M", // <-- Child attached to parent (flattened)
status_desc: "Married" // <-- Child attached to parent (flattened)
}
So basically I need to attach my child info to the parent.
UPDATE
I was able to get one of the properties flattened with the parent by using a custom JsonDeserializer as follows
public class nameDeserializer extends JsonDeserializer<String> {
#Overrde
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
PersonStatus st = p.valueAs(PersonStatus.class)
return st.getStatusCd();
}
This works for only one property set with #JsonDeserialize(using = nameDeserializre.class) per class.... once I attach two deserializers to two properties, I start getting errors about json exceptions.
Thank you
Try something like this
public class Person {
private String name;
private int age;
private Status status;
//your getter setter
}
public class Status {
private String cd;
private String desc;
// getter setter
}

How can I prevent an empty JSON object from being deserialized to a Java bean? [duplicate]

At the deserialization process (which as I understand is the process of converting JSON data into a Java Object), how can I tell Jackson that when it reads a object that contains no data, it should be ignored?
I'm using Jackson 2.6.6 and Spring 4.2.6
The JSON data received by my controller is as follows:
{
"id": 2,
"description": "A description",
"containedObject": {}
}
The problem is that the object "containedObject" is interpreted as is and it's being instantiated. Therefore, as soon as my controller reads this JSON data, it produces an instance of the ContainedObject object type but I need this to be null instead.
The easiest and fastest solution would be that in the JSON data received, this value be null like this:
{
"id": 2,
"description": "A description",
"containedObject": null
}
But this isn't possible since I'm not in control of the JSON data that is sent to me.
Is there an annotation (like this explained here) that works for the deserialization process and could be helpfull in my situation?
I leave a representation of my classes for more information:
My entity class is as follows:
public class Entity {
private long id;
private String description;
private ContainedObject containedObject;
//Contructor, getters and setters omitted
}
And my contained object class as follows:
public class ContainedObject {
private long contObjId;
private String aString;
//Contructor, getters and setters omitted
}
I would use a JsonDeserializer. Inspect the field in question, determine, if it is emtpy and return null, so your ContainedObject would be null.
Something like this (semi-pseudo):
public class MyDes extends JsonDeserializer<ContainedObject> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
//read the JsonNode and determine if it is empty JSON object
//and if so return null
if (node is empty....) {
return null;
}
return node;
}
}
then in your model:
public class Entity {
private long id;
private String description;
#JsonDeserialize(using = MyDes.class)
private ContainedObject containedObject;
//Contructor, getters and setters omitted
}
Hope this helps!
You can implement a custom deserializer as follows:
public class Entity {
private long id;
private String description;
#JsonDeserialize(using = EmptyToNullObject.class)
private ContainedObject containedObject;
//Contructor, getters and setters omitted
}
public class EmptyToNullObject extends JsonDeserializer<ContainedObject> {
public ContainedObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
long contObjId = (Long) ((LongNode) node.get("contObjId")).numberValue();
String aString = node.get("aString").asText();
if(aString.equals("") && contObjId == 0L) {
return null;
} else {
return new ContainedObject(contObjId, aString);
}
}
}
Approach 1 : This is mostly used. #JsonInclude is used to exclude properties with empty/null/default values.Use #JsonInclude(JsonInclude.Include.NON_NULL) or #JsonInclude(JsonInclude.Include.NON_EMPTY) as per your requirement.
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
private String empId;
private String firstName;
#JsonInclude(JsonInclude.Include.NON_NULL)
private String lastName;
private String address;
private String emailId;
}
More info about the jackson annotations : https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations
Approach 2 : GSON
use GSON (https://code.google.com/p/google-gson/)

How to deserialize json object into flatten format direct field?

I have the following structure:
public class User {
private Account account;
//constuctors, getters and setters
}
public class Account {
private String id;
private String description;
//constructor, getters and setters
}
When I performing the request I need to create the following JSON structure:
{
"account":
{
"id": "1",
"description": "Some description"
}
}
But I want to specify this information in a short way and ignore(left 'null') the 'description' field in the following way:
{
"account": "1" // I want to set directly the id field in the account object.
}
How may I do it? I tried #JsonCreator annotation and #JsonUnwrapped but without result.
You can use a custom deserializer
public class AccountFromIdDeserializer extends StdDeserializer<Account> {
public AccountFromIdDeserializer() { this(null);}
protected AccountFromIdDeserializer(Class<Account> type) { super(type);}
#Override
public Account deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
Account account = new Account();
account.setId(parser.getValueAsString());
return account;
}
}
And use on account node of User using #JsonDeserialize
public class User {
#JsonDeserialize(using = AccountFromIdDeserializer.class)
private Account account;
//constuctors, getters and setters
}
Finally I used #JsonCreator annotation and created two constructors:
#JsonCreator
public Account(#JsonProperty("id") String id, #JsonProperty("description") String description) {
this.id = id;
this.description = description;
}
#JsonCreator
public Account(String id) {
this.id = id;
}

Jackson JSON field mapping capitalization?

I'm not clear how jackson deals with capitalization in mapping fields. If anyone could help I'd appreciate it.
{"user":{"username":"user#host.com","password":"pwd","sendercompid":"COMPID","service":{"host":"address","port":6666,"service":"S1","serviceAsString":"s1"}},"MDReqID":"ghost30022","NoRelatedSym":1,"Symbol":["GOOG"],"MarketDepth":"0","NoMDEntryTypes":3,"MDEntryType":["0","1","2"],"SubscriptionRequestType":"1","AggregatedBook":"N"}:
Above is my json, below is my exception...
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "MDReqID" (class com.myco.qa.fixrest.MarketDataRequest), not marked as ignorable (10 known properties: , "mdreqID", "marketDepth", "user", "subscriptionRequestType", "aggregatedBook", "mdentryType", "symbol", "mdupdateType", "noRelatedSym", "noMDEntryTypes"])
Above is my exception, below is my class...
public class MarketDataRequest {
private User user;
private String MDReqID;
private char SubscriptionRequestType;
private int MarketDepth;
private int MDUpdateType;
private char AggregatedBook;
private int NoMDEntryTypes;
private ArrayList<Character> MDEntryType;
private int NoRelatedSym;
private ArrayList<String> Symbol;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getMDReqID() {
return MDReqID;
}
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
public char getSubscriptionRequestType() {
return SubscriptionRequestType;
}
public void setSubscriptionRequestType(char subscriptionRequestType) {
SubscriptionRequestType = subscriptionRequestType;
}
... et cetera
Since your setter method is named setMDReqID(…) Jackson assumes the variable is named mDReqID because of the Java naming conventions (variables should start with lower case letters).
If you really want a capital letter use the #JsonProperty annotation on the setter (or - for serialization - on the getter) like this:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
You can also do
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
on the class to capitalise all property names in the JSON message
Add #JsonProperty on the setter that matches the property name in your received JSON string:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
Additionally add #JsonProperty annotation to the getter as well for your output to appear in the conventional format:
#JsonProperty("mDReqID")
public String getMDReqID() {
return MDReqID;
}
Now you can name your variable whatever you like:
private String mdReqID;
I solve this problem by:
#Getter
#Setter
static class UserInfo {
//#JsonProperty("UUID")
private String UUID = "11";
private String UserName = "22";
private String userName = "33";
private String user_Name = "44";
private String user_name = "55";
private String User_name = "66";
private boolean HasDeleted=true;
private boolean hasDeleted=true;
private boolean has_Deleted=true;
private boolean has_deleted=true;
private boolean HAS_DELETED=true;
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String s = objectMapper.writeValueAsString(new UserInfo());
System.out.println(s);
UserInfo userInfo = objectMapper.readValue(s, UserInfo.class);
System.out.println(objectMapper.writeValueAsString(userInfo));
}
output:
{"UUID":"11","UserName":"22","userName":"33","user_Name":"44","user_name":"55","User_name":"66","HasDeleted":true,"hasDeleted":true,"has_Deleted":true,"has_deleted":true,"HAS_DELETED":true}
I face the same problem , after have try UpperCamelCaseStrategy but still this error occurred , the strategy made my field pContent to ObjectMapper property Pcontent, as not want to add #JsonProperty for every field, simply use gson instead at last
Use JsonNaming Annotation to get all Class Field Names in Proper Case
Use lombok.Data Annotation to automatically make it work without adding getters and setters in your class
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import lombok.Data;
#JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class)
#Data

'dynamic'-like java annotations?

I have a pojo that is dependent on annotations. It has predefined fields as well as a Set that contains user provided fields:
public class MyPOJO implements Document {
private String id;
private LocalString name;
private LocalString desc;
private List<Field> fields;
public MyPOJO(final String id,
final LocalString name,
final LocalString desc,
final List<Field> fields) {
this.id = id;
this.name = name;
this.desc = desc;
this.fields = fields;
}
public String getId() {
return id;
}
#Indexed(searchable = false, stored = true)
public LocalString getName() {
return name;
}
#Indexed(searchable = true)
public LocalString getDescription() {
return desc;
}
public List<Field> getFields() {
return fields;
}
}
MyPOJO is a 'generic' object, ie, the developer (or consumer) of MyPOJO has fields that are not predefined in MyPOJO and therefore the developer needs to place these additional fields the in attribute 'fields'. The problem arises from the fact that each object in the Set fields needs to have its own annotations to indicate whether the particular field is either stored or searchable in order to remain consistent with the predefined attributes, such as name.
I can think of two options:
For each additional field, the developer will have to create an
anonymous class implementing the interface Field and inside this
anonymous class, the developer will declare the applicable
annotations.
the Set 'fields' contains a complex object of fieldname, fieldvalue
and annotations as shown below. I can't figure out how to invoke the constructor for Field. The below code does not compile but it is intended as pseudo-code to signify what I am trying to do.
Field myfield1 = new Field("dateofBirth", new Date(), new ArrayList({Index.stored, Index.searchable});
Field myfield2 = new Field("model", "330i", new ArrayList({Index.stored});
There is no construct to pass annotations as a parameter: new ArrayList({Index.stored}.
public class Field {
private String name;
private Object value;
Collection<Annotation> annotations;
public Field(final String name, final Object value, Collection<Annotation> annotations;) {
this.name = name;
this.value = value;
this.annotations = Collections.unmodifiableCollection(annotations);
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
I'm not particularly excited with either option and hoping someone can give me some pointers
If you need an extensible object model, I'd say a POJO design is just setting yourself up for extra work as opposed to exposing a metamodel.
That said, what you could do is have clients of the API subclass MyPOJO, and annotate the properties they define in their subclasses. You would then use reflection to go through all JavaBeans properties of the objects you're receiving and determine the annotations on the getters - similarly to how JPA works.

Categories

Resources