I am trying to build following json structure via my objects
{
Id:"123",
Name:"me",
Attributes:[{x:"test1",value:{value1:"1"},{x:"test2",value:[{abc:"somevalue"}]}}
]
Attributes size will be fixed and value of x would be same in each request.
What would be ideal structure of classes in jackson mapping
Seems like a structure like this would do.
Of course you'll need some additional annotations to ensure the fields have the proper names, and you'll also want to ass getters and setters.
class Outer{
String id;
String name;
List<Attribute> attributes;
}
class Attribute{
String x;
Map<String, String> values;
}
Related
Is it possible to rename JSON output fields in an object an arbitrary number of times when outputting with Jackson?
I can use a one-time JsonProperty as shown here,
How to map JSON field names to different object field names?
But suppose I have a single class which is used in multiple outputs. In each output, I want to have the flexibility of defining which name(s) to change.
public class Study implements Serializable {
// Can vary as "id" / "studyId" depending on call
private int id;
// Can vary as "description" / "studyDescription" / "studyDesc" depending on call
private String description;
}
Or do I need to create new objects for each case?
Do refer: https://www.baeldung.com/json-multiple-fields-single-java-field
It's as simple as using #JsonAlias in combination with #JsonProperty annotation as below:
public class Study implements Serializable {
// Can vary as "id" / "studyId" depending on call
#JsonProperty("id")
#JsonAlias("studyId")
private int id;
// Can vary as "description" / "studyDescription" / "studyDesc" depending on call
private String description;
}
PS: Using #JsonProperty twice didn't work :D
This topic is a little bit more complicated then in a title.
Let's assume that we want to model an entity. This is something like KYC informations (name,surname, address etc). I could model this in simple way in one class like:
public class KYCInfo {
private KYCInfoType type;
private String firstName;
private String lastName;
private Address personalAddress;
private Address buisnessAddress;
private String country;
private String state;
private LocalDate dateOfBirth;
private String personalIdNumber;
}
As you see in code above, this KYC can be of different type. Actually two values can be in that type - buisness and individual. For business, buisnessAddress field is required, for individual personalIdNumber is required. Additionaly some of this fields will be required depending on country field. State field is for US but not for European countries. Placing all of this fields in one class seems to be inappropriate - every instance, depending on field would have a lot of null values. I could create separate classes for BuisnessKYCInfo and IndividualKYCInfo for example. But then I would have some duplications in fields (lets say that firstName,lastName and some other fields are the same for both classes). I could create some abstraction with common fields. Like :
public abstract class KYCInfo {
private String firstName;
private String lastName;
}
Now imagine that this is a simple DTO class and in some moment i process it somehow in a method processKYCInfo(). When I have two classes BuisnessKYCInfoandIndividualKYCInfothen I would need to have two methods
``processKYCInfo(BuisnessKYCInfo kycInfo) and processKYCInfo(IndividualKYCInfo kycInfo). This method will do the same operation, but will collect info from different fields. Now imagine, that you have more type than individual or buissness. Or as i wrote before, additional 'type' comes in like country. Now I would need to have 25 countries, some of them have fields specific only for that country. With the same approach like before I would have 25 methods doing almost the same. This also seems to be inappropriate. What other option do I have to model this ? Maybe some data structure, maybe some Map ? What is best approach of doing this ? How can I do it in more generic way ?
Here is my approach:
Following is the data structure I'll use:-
public class KYCInfo {
private KYCInfoType type;
private Map<String, String> name;
private Map<String, String> address;
private String country;
private String state;
private LocalDate dateOfBirth;
private String personalIdNumber;
public KYCInfo(){
name = new HashMap<>();
address = new HashMap<>();
}
So what is the advantage of this approach:-
Instead of creating multiple same type of attributes, create a family. e.g. name will be the family for 'firstName', 'middleName', 'lastName'. The pair will be like <'firstName','Bob'> ,<'lastName','Marley'> etc. In this way you can have either of them or all of them.
Similarly for other attributes like address. The pair can be like <'personalAddress','some value'> , <'buisnessAddress','some value'>
Each record can have their own categories for a family.
Suppose I have JSON that looks something like this:
{ "key1":1, "key2":2, "key3":3 }
Where the number of "key-n" fields is unknown, but are consecutively numbered starting at 1. I wish to deserialize it into an object as follows:
public class MyPojo {
private List<Integer> keys;
}
That is, keys.get(0) corresponds to the key1 field, and so on. The JSON may have other non-"key-n" fields as well.
I had been under the impression that something like
public class MyPojo {
#JsonUnwrapped #JsonDeserialize(using = KeyDeserializer.class) private List<Integer> keys;
}
where KeyDeserializer is a JsonDeserializer would just extract all of the "key-n" fields, would work; however, I had discovered that the deserializer isn't being invoked because the JSON lacks a field named key.
Since the JSON is third-party, I can't really try to modify the JSON, so I am wondering if there are any alternate approaches to this problem.
There is a question on my mind for a while. Let's say we have the following classes:
public Class Person{
String name;
String address;
String description;
}
public Class PersonFacade{
String name;
String address;
String desc;
}
as you can see the only difference between these two classes are the name of one variable. My question is what is the best way to write a helper class to map the values of one object to another object. Let's assume we have the following:
Person person = new Person();
person.name="name1";
person.address="address1";
person.description="description1";
I want to write a class that is supposed to do the following (let's call it Transformer class)
PersonFacade personFacade = new PersonFacade();
TransformClass.transformFrom(person, personFacade);
What I want this TransformClass.transformFrom() method to do is the follwoing:
based on the similarity of the variable names, assign the value of the variable from "FromClass" to "ToClass"
so in our case, I want it to assign personFacade.name = "name1", personFacade.address="adderss1" and personFacade.desc = "description1" (this last one seems harder to accomplish, but let's try)
Any ideas?
You can use Dozer:
Dozer is a Java Bean to Java Bean mapper that recursively copies data
from one object to another. Typically, these Java Beans will be of
different complex types.
Dozer supports simple property mapping, complex type mapping,
bi-directional mapping, implicit-explicit mapping, as well as
recursive mapping. This includes mapping collection attributes that
also need mapping at the element level.
Look at this: http://dozer.sourceforge.net/
It's a great JavaBean Mapper.
Here the "Getting Started":
http://dozer.sourceforge.net/documentation/gettingstarted.html
Perhaps you can write your own Annotation class in order to create the relationship between the classes. So, for example
public Class Person{
#MyAnnotation(id='name')
String name;
#MyAnnotation(id='addr')
String address;
#MyAnnotation(id='desc')
String description;
}
public Class PersonFacade{
#MyAnnotation(id='name')
String name;
#MyAnnotation(id='addr')
String address;
#MyAnnotation(id='desc')
String desc;
}
Then in your TransformClass, you simply need to iterate through the annotations, find a match and set the corresponding field value with the help of Reflection.
Suppose I have a json object that looks like:
{
id: 1,
name: "john doe"
spouse: 2
}
and the class I want it to deserialize it to:
class Person{
private Long id;
private String name;
private Person spouse;
//getters/setters
}
Is there any way to tell jackson to expand the spouse: 2 property into a new Person POJO with id=2 when deserializing the JSON?
I have run into this issue as a result of deserializing JSON into persistent entities and would like to be able to easily persist the relationships between these entities.
Aside from a full deserializer, there is a simpler way: define a POJO with a single int-arg constructor like so:
class Person {
int id;
public Person(int id) {
this.id = id;
}
}
This actually works, as Jackson will try to find limited number of special constructors (single-arg public constructors that that take String, int, long, double or boolean).
You can optionally also denote these with #JsonCreator -- and if constructor is not public, you must do it, to make it discoverable. But for public ctors this is not needed.
It is impossible of course for Jackson to infer a fully populated Person object representing the spouse from the number 2. You would likely need to register a custom deserializer that checks if the input is an integer, and if so, looks up the spouse from wherever it is stored. I have not done this kind of thing for classes that contain references to themselves (e.g. your Person contains a Person) so I can only give you rough guidance.
I believe this may only work with Jackson version 1.9 or later. Basically, you can register a module with the object mapper that tells Jackson to use a custom deserializer.
SimpleModule module = new SimpleModule("PeopleModule", new Version(1, 1, 0, null);
module.addDeserializer(Person.class, new JacksonPersonDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Alternately on the Person class itself, you can do something like:
class Person {
#JsonDeserialize(using=JacksonPersonDeserializer.class)
Person spouse;
}
This works before 1.9 but pollutes your object. Either way, you will need to write a custom deserializer.