I have a JSON looks like the following:
{
"name": "John",
"age": 20,
"skills": [
]
}
the skills if it's not empty looks like the following:
{
"skills": [
"skill_1": {
},
"skill_2": {
}]
}
and I need to deserialize this JSON to POJO:
public class Profile {
public String name;
public int age;
#JsonDeserialize(using = SkillsMapDeserializer.class)
public Map<String, Skill> skills;
}
public class Skill {
public String skillName;
public int age;
}
and my SkillsMapDeserializer looks like the following:
public class SkillsMapDeserializer extends JsonDeserializer<Map<String, Skill>> {
#Override
public Map<String, Skill> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final Map<String, Skill> map = jsonParser.readValueAs(new TypeReference<Map<String, Skill>>() {
});
if (map == null) {
return new HashMap<>();
}
return map;
}
}
if the skills aren't empty all works fine, but if the skills are an empty array I get an exception that looks like the following:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.LinkedHashMap<java.lang.Object,java.lang.Object>` out of START_ARRAY token
How can I fix this issue?
From your json data, it seem skills is an array of object.
"skills": [],
"skills": [
"skill_1": {},
"skill_2": {}
]
But your java define it as Map
public Map<String, Skill> skills;
That's why you got an exception when trying convert array to map directly.
If you can't change the POJOs Profile, you should have an mediate step to convert list to Map.
public class SkillsMapDeserializer extends JsonDeserializer<Map<String, Skill>> {
#Override
public Map<String, Skill> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final List<Map<String,Skill>> skills = jsonParser.readValueAs(new TypeReference<List<Map<String,Skill>>>>() {
});
return functionConvertListToMapWithParam(skills);
}
}
skills is not a map. it should be list of objects. try to modify your POJO like below:-
public class Profile {
#JsonProperty("name")
private String name;
#JsonProperty("age")
private Integer age;
#JsonProperty("skills")
private List < Object > skills = null;
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("age")
public Integer getAge() {
return age;
}
#JsonProperty("age")
public void setAge(Integer age) {
this.age = age;
}
#JsonProperty("skills")
public List < Object > getSkills() {
return skills;
}
#JsonProperty("skills")
public void setSkills(List < Object > skills) {
this.skills = skills;
}
}
Related
I have gone through the threads from SOF which talks about getting nested JSON using GSON. Link 1 Link 2. My JSON file is as shown below
{
"Employee_1": {
"ZipCode": 560072,
"Age": 50,
"Place": "Hawaii",
"isDeveloper": true,
"Name": "Mary"
},
"Employee_2": {
"ZipCode": 560072,
"Age": 80,
"Place": "Texas",
"isDeveloper": true,
"Name": "Jon"
}
}
my classes are as shown below
public class Staff {
String Employee_1 ;
}
class addnlInfo{
String Name;
String Place;
int Age;
int Zipcode;
boolean isDeveloper;
}
The deserializer class which I built is as shown below
class MyDeserializer implements JsonDeserializer<addnlInfo>{
public addnlInfo deserialize1(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("Employee_1");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, addnlInfo.class);
}
#Override
public TokenMetaInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
// TODO Auto-generated method stub
return null;
}
The main file
Gson gson = new GsonBuilder()
.registerTypeAdapter(addnlInfo.class, new MyDeserializer())
.create();
String jsonObject= gson.toJson(parserJSON);
addnlInfo info= gson.fromJson(jsonObject, addnlInfo .class);
System.out.println(info.Age + "\n" + info.isDeveloper + "\n" + info.Name + "\n" + info.Place);
Staff parentNode = gson.fromJson(jsonObject, Staff.class);
System.out.println(parentNode.Employee_1);
The problem:
My Subparent element (e.g. 'Employee_1') keeps changing. Do I have to construct multiple deserializers?
Also, I get "Expected a string but was BEGIN_OBJECT" which I understand as we use nestedJSON.
I am not sure how your classes translate to your JSON, but you are making this too complex.
I renamed fields and class names to adhere to Java standards.
Main.java
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
public class Main {
public static void main(String[] args) {
Map<String, Staff> employees = new LinkedHashMap<String, Staff>();
employees.put("Employee_1", new Staff(new Info("Mary", "Hawaii", 50, 56072, true)));
employees.put("Employee_2", new Staff(new Info("Jon", "Texas", 80, 56072, true)));
String jsonString = new GsonBuilder().setPrettyPrinting().create().toJson(employees);
System.out.println("# SERIALIZED DATA:");
System.out.println(jsonString);
Type mapOfStaff = new TypeToken<Map<String, Staff>>() {}.getType();
Map<String, Staff> jsonObject = new Gson().fromJson(jsonString, mapOfStaff);
System.out.println("\n# DESERIALIZED DATA:");
for (Entry<String, Staff> entry : jsonObject.entrySet()) {
System.out.printf("%s => %s%n", entry.getKey(), entry.getValue());
}
}
}
Staff.java
public class Staff {
private Info info;
public Staff(Info info) {
this.info = info;
}
public Info getInfo() {
return info;
}
public void setInfo(Info info) {
this.info = info;
}
#Override
public String toString() {
return String.format("Staff [info=%s]", info);
}
}
Info.java
public class Info {
private String name;
private String place;
private int age;
private int zipcode;
private boolean developer;
public Info(String name, String place, int age, int zipcode, boolean developer) {
this.name = name;
this.place = place;
this.age = age;
this.zipcode = zipcode;
this.developer = developer;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPlace() {
return place;
}
public void setPlace(String place) {
this.place = place;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getZipcode() {
return zipcode;
}
public void setZipcode(int zipcode) {
this.zipcode = zipcode;
}
public boolean isDeveloper() {
return developer;
}
public void setDeveloper(boolean developer) {
this.developer = developer;
}
#Override
public String toString() {
return String.format(
"Info [name=%s, place=%s, age=%d, zipcode=%d, developer=%b]",
name, place, age, zipcode, developer
);
}
}
Output
# SERIALIZED DATA:
{
"Employee_1": {
"info": {
"name": "Mary",
"place": "Hawaii",
"age": 50,
"zipcode": 56072,
"developer": true
}
},
"Employee_2": {
"info": {
"name": "Jon",
"place": "Texas",
"age": 80,
"zipcode": 56072,
"developer": true
}
}
}
# DESERIALIZED DATA:
Employee_1 => Staff [info=Info [name=Mary, place=Hawaii, age=50, zipcode=56072, developer=true]]
Employee_2 => Staff [info=Info [name=Jon, place=Texas, age=80, zipcode=56072, developer=true]]
I have a String in my servlet which is of the following format.
{
"name": "Jam",
"noOfBooksRequired": "2",
"type": "Type 1",
"bookName": [
"The Magic",
"The Power"
]
}
where the bookName is an array. I want to access the values in the array and populate in the bean. But, when I try to convert the string to jsonobject, I am getting the following exception because bookName is an array com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY This is how I am trying to do it
JSONObject js= new JSONObject();
String inputData= request.getParameter("inputData");
HashMap<String, String> hmap= new HashMap<String, String>();
Type type = new TypeToken<HashMap<String, String>>(){}.getType();
hmap = gson.fromJson(inputData, type);
js.putAll(hmap);
What I am doing is, I convert the string to a map and then add it to the JSONObject.
Since there are many json serializers and not sure which is the best. Right now, I have net.sf.json.JSONObject and com.google.gson.JsonObject
Can someone help me to get this solved.
Thanks in advance
You can map your JSON to a POJO.
If the book will have more attributes besides the name, you'll need two POJOs, as you can see below.
A POJO for the book:
class Book {
private String name;
private String author;
public Book() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
And a POJO for the shelf, which have a list of books:
class Shelf {
private String name;
private Integer noOfBooksRequired;
private String type;
private List<Book> books;
public Shelf() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNoOfBooksRequired() {
return noOfBooksRequired;
}
public void setNoOfBooksRequired(Integer noOfBooksRequired) {
this.noOfBooksRequired = noOfBooksRequired;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}
Your JSON will look like this:
{
"name": "Jam",
"noOfBooksRequired": "2",
"type": "Type 1",
"books": [
{"name": "The Magic", "author": "John Doe"},
{"name": "The Power", "author": "Jane Doe"}
]
}
And then you can use Gson to parse your JSON:
Gson gson = new Gson();
Shelf shelf = gson.fromJson(inputData, Shelf.class);
Update
Considering your JSON looks like this (the book can be represented as a String):
{
"name": "Jam",
"noOfBooksRequired": "2",
"type": "Type 1",
"books": [
"The Magic",
"The Power"
]
}
Only one POJO with a list of String is enough:
class Shelf {
private String name;
private Integer noOfBooksRequired;
private String type;
private List<String> books;
public Shelf() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNoOfBooksRequired() {
return noOfBooksRequired;
}
public void setNoOfBooksRequired(Integer noOfBooksRequired) {
this.noOfBooksRequired = noOfBooksRequired;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<String> getBooks() {
return books;
}
public void setBooks(List<String> books) {
this.books = books;
}
}
Given I have the following json:
{
"Company": {
"name": "cookieltd",
"type": "food",
"franchise_location": [
{
"location_type": "town",
"address_1": "5street"
},
{
"location_type": "village",
"address_1": "2road"
}
]
}
}
How can it be binded to the following object classes using Jackson?:
1) Company class
public class Company
{
String name, type;
List<Location> franchise_location = new ArrayList<Location>();
[getters and setters]
}
2) Location class
public class Location
{
String location_type, address_1;
[getters and setters]
}
I have done:
String content = [json above];
ObjectReader reader = mapper.reader(Company.class).withRootName("Company"); //read after the root name
Company company = reader.readValue(content);
but I am getting:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "franchise_location"
As far as I can tell, you are simply missing an appropriately named getter for the field franchise_location. It should be
public List<Location> getFranchise_location() {
return franchise_location;
}
(and the setter)
public void setFranchise_location(List<Location> franchise_location) {
this.franchise_location = franchise_location;
}
Alternatively, you can annotate your current getter or field with
#JsonProperty("franchise_location")
private List<Location> franchiseLocation = ...;
which helps to map JSON element names that don't really work with Java field name conventions.
The following works for me
public static void main(String[] args) throws Exception {
String json = "{ \"Company\": { \"name\": \"cookieltd\", \"type\": \"food\", \"franchise_location\": [ { \"location_type\": \"town\", \"address_1\": \"5street\" }, { \"location_type\": \"village\", \"address_1\": \"2road\" } ] } }";
ObjectMapper mapper = new ObjectMapper();
ObjectReader reader = mapper.reader(Company.class).withRootName(
"Company"); // read after the root name
Company company = reader.readValue(json);
System.out.println(company.getFranchise_location().get(0).getAddress_1());
}
public static class Company {
private String name;
private String type;
private List<Location> franchise_location = new ArrayList<Location>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Location> getFranchise_location() {
return franchise_location;
}
public void setFranchise_location(List<Location> franchise_location) {
this.franchise_location = franchise_location;
}
}
public static class Location {
private String location_type;
private String address_1;
public String getLocation_type() {
return location_type;
}
public void setLocation_type(String location_type) {
this.location_type = location_type;
}
public String getAddress_1() {
return address_1;
}
public void setAddress_1(String address_1) {
this.address_1 = address_1;
}
}
and prints
5street
my solution for JSON is always GSON, you can do some research on that, as long as you have the correct structure of class according to the JSON, it can automatically transfer from JSON to object:
Company company = gson.fromJson(json, Company.class);
GSON is so smart to do the convertion thing!
enjoy GSON !
I am a beginner of Jackson. How can I create a JSON message like this using Java?
{
"name": "John",
"age": "40",
"family": {
"parents_name": [
"David",
"Susan"
],
"children": "yes",
"children_names": [
"Peter",
"Mary"
]
}
}
Create a Person class in Java, with properties such as getName(), getAge() and so on. Then Jackson can create that JSON for you automatically, from your Person object.
The easiest way to do this for a beginner is to eliminate unnecessary nesting and rely on Jackson's default object binding.
You would create a class like this:
public class Person {
private String name;
private int age;
private List<String> parentNames;
private List<String> childrenNames;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<String> getParentNames() {
return parentNames;
}
public void setParentNames(List<String> parentNames) {
this.parentNames = parentNames;
}
public List<String> getChildrenNames() {
return childrenNames;
}
public void setChildrenNames(List<String> childrenNames) {
this.childrenNames = childrenNames;
}
}
Then you can instantiate a Person from JSON like this:
Person p = ObjectMapper.readValue(jsonString, Person.class);
Note that the JSON you have in your example won't work with this object for three reasons:
The Person class has no Family object. I felt that adds unnecessary complexity. If you want that, create a separate Family class, and Person would contain a Family member (no pun intended).
I don't have a boolean for children because that can be deduced from the length of the childrenNames list.
The JSON will need to have childrenNames and parentNames rather than children_names and parents_name. If you want those, add #JsonProperty with the desired property names on the getters and setters for those values.
I gather from your comments to Vidya's solution that your looking for more flexibility than you get can get with the default binding.
Jackson allows you to create your own custom serializer. For example:
public class Person {
private String name;
private int age;
private List<String> parentsName;
private List<String> childrenNames;
public Person(String name, List<String> parentsName) {
this(name, parentsName, -1, Collections.<String>emptyList());
}
public Person(String name, List<String> parentsName, int age) {
this(name, parentsName, age, Collections.<String>emptyList());
}
public Person(String name, List<String> parentsName, int age, List<String> childrenNames) {
this.name = name;
this.age = age;
this.parentsName = parentsName;
this.childrenNames = childrenNames;
}
private void serialize(JsonGenerator generator, SerializerProvider arg2) throws IOException {
generator.writeStartObject();
generator.writeObjectField("name", name);
if (age >= 0)
generator.writeNumberField("age", age);
// start family subset
generator.writeObjectFieldStart("family");
generator.writeArrayFieldStart("parents_name");
for (String parent : parentsName) {
generator.writeObject(parent);
}
generator.writeEndArray();
generator.writeObjectField("children", (childrenNames.isEmpty() ? "no" : "yes"));
generator.writeArrayFieldStart("children_names");
for (String child : childrenNames) {
generator.writeObject(child);
}
generator.writeEndArray();
generator.writeEndObject();
// end family subset
generator.writeEndObject();
}
public static JsonSerializer<Person> createJsonSerializer() {
return new JsonSerializer<Person>() {
#Override
public void serialize(Person me, JsonGenerator generator, SerializerProvider arg2) throws IOException, JsonProcessingException {
me.serialize(generator, arg2);
}
};
}
public static void main(String[] args) throws IOException {
List<String> parentsName = Arrays.<String>asList("David", "Susan");
List<String> childrenNames = Arrays.<String>asList("Peter", "Mary");
Person person = new Person("John", parentsName, 40, childrenNames);
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule("PersonModule", new Version(1, 0, 0, null));
simpleModule.addSerializer(Person.class, Person.createJsonSerializer());
// pretty output for debugging
mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
mapper.registerModule(simpleModule);
System.out.println("Person json: ");
System.out.println(mapper.writeValueAsString(person));
}
}
This gives you increased flexibility in two ways:
You can apply conditional logic in serialization
You can have multiple custom serializers
The downsides are fairly obvious
More complicated
More time to implement. The default bindings were almost free. This solution is not.
I would like deserialize my custom serialized objects. My objects are basically consisting a simple Pair implementation.
class School{
Integer id;
String schoolName;
}
class Student{
Integer id;
Integer schoolId;
String studentName;
}
#JsonSerialize(using=PairSerializer.class)
public class Pair<V,K>{
V v;
K k;
}
Here is the result
[
{
"v":{
"id":1,
"schoolId":3,
"studentName":"O. Bas"
},
"k":{
"id":3,
"schoolName":"School 3"
}
},
{
"v":{
"id":2,
"schoolId":3,
"studentName":"C. Koc"
},
"k":{
"id":3,
"schoolName":"School 3"
}
}
]
v and k as field name in json is pretty ugly. That is why I have written a custom serializer as this:
#Override
public void serialize(Pair pair, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL,pair.getK().getClass().getSimpleName() ), pair.getK());
jsonGenerator.writeObjectField(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL,pair.getV().getClass().getSimpleName() ), pair.getV());
jsonGenerator.writeEndObject();
}
The result is exactly what I want. v and k field names are replaced by their class names.
[
{
"school":{
"id":3,
"schoolName":"School 3"
},
"student":{
"id":1,
"schoolId":3,
"studentName":"O. Bas"
}
},
{
"school":{
"id":3,
"schoolName":"School 3"
},
"student":{
"id":2,
"schoolId":3,
"studentName":"C. Koc"
}
}
]
Here is the my question. How can I deserialize my json string to List<Pair<V, K> ? The real problem is that V and K are depends on the deserialized context it might vary as Student, School or another pair implementation.
public class PairDeserializer extends JsonDeserializer<Pair> {
public PairDeserializer() {
}
#Override
public Pair deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// I need to Deserialized generic type information of Pair
}
}
I think, you should create your own PropertyNamingStrategy. For example see my simple implementation:
class MapTransformNamingStrategy extends LowerCaseWithUnderscoresStrategy {
private static final long serialVersionUID = 1L;
private Map<String, String> mapping;
public MapTransformNamingStrategy(Map<String, String> mapping) {
this.mapping = mapping;
}
#Override
public String translate(String property) {
if (mapping.containsKey(property)) {
return mapping.get(property);
}
return property;
}
}
Now you can use it in this way:
Map<String, String> mapping = new HashMap<String, String>();
mapping.put("k", "student");
mapping.put("v", "school");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(new MapTransformNamingStrategy(mapping));
//etc
Example JSON output:
{ "school" : { "id" : 1,
"schoolName" : "The Best School in the world"
},
"student" : { "id" : 1,
"schoolId" : 1,
"studentName" : "Arnold Shwarz"
}
}
EDIT
Because my answer is not clear for everyone I present full example source code which serialize Java POJO objects into JSON and "vice versa".
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy;
public class JacksonProgram {
#SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
List<Pair<Student, School>> pairs = createDataForSerialization();
Map<String, String> mapping = createSchoolStudentMapping();
JsonConverter jsonConverter = new JsonConverter(mapping);
String json = jsonConverter.toJson(pairs);
System.out.println("JSON which represents list of pairs:");
System.out.println(json);
List<Pair<Student, School>> value = jsonConverter.fromJson(json, List.class);
System.out.println("----");
System.out.println("Deserialized version:");
System.out.println(value);
}
private static Map<String, String> createSchoolStudentMapping() {
Map<String, String> mapping = new HashMap<String, String>();
mapping.put("k", "student");
mapping.put("v", "school");
return mapping;
}
private static List<Pair<Student, School>> createDataForSerialization() {
List<Pair<Student, School>> pairs = new ArrayList<Pair<Student, School>>();
pairs.add(new Pair<Student, School>(new Student(1, 3, "O. Bas"), new School(3, "School 3")));
pairs.add(new Pair<Student, School>(new Student(2, 4, "C. Koc"), new School(4, "School 4")));
return pairs;
}
}
class JsonConverter {
private Map<String, String> mapping;
private ObjectMapper objectMapper;
private JsonFactory jsonFactory;
public JsonConverter(Map<String, String> mapping) {
this.mapping = mapping;
initJsonObjects();
}
private void initJsonObjects() {
objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(new MapTransformNamingStrategy(mapping));
jsonFactory = new JsonFactory();
}
public String toJson(Object object) throws Exception {
StringWriter stringWriter = new StringWriter();
JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter);
objectMapper.writeValue(jsonGenerator, object);
return stringWriter.toString();
}
public <T> T fromJson(String json, Class<T> expectedType) throws Exception {
JsonParser jsonParser = jsonFactory.createJsonParser(json);
return objectMapper.readValue(jsonParser, expectedType);
}
}
class MapTransformNamingStrategy extends LowerCaseWithUnderscoresStrategy {
private static final long serialVersionUID = 1L;
private Map<String, String> mapping;
public MapTransformNamingStrategy(Map<String, String> mapping) {
this.mapping = mapping;
}
#Override
public String translate(String property) {
if (mapping.containsKey(property)) {
return mapping.get(property);
}
return property;
}
}
class School {
private Integer id;
private String schoolName;
public School() {
}
public School(Integer id, String schoolName) {
this.id = id;
this.schoolName = schoolName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
#Override
public String toString() {
return "School [id=" + id + ", schoolName=" + schoolName + "]";
}
}
class Student {
private Integer id;
private Integer schoolId;
private String studentName;
public Student() {
}
public Student(Integer id, Integer schoolId, String studentName) {
this.id = id;
this.schoolId = schoolId;
this.studentName = studentName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getSchoolId() {
return schoolId;
}
public void setSchoolId(Integer schoolId) {
this.schoolId = schoolId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
#Override
public String toString() {
return "Student [id=" + id + ", schoolId=" + schoolId + ", studentName=" + studentName
+ "]";
}
}
class Pair<V, K> {
private V v;
private K k;
public Pair() {
}
public Pair(V v, K k) {
this.v = v;
this.k = k;
}
public V getV() {
return v;
}
public void setV(V v) {
this.v = v;
}
public K getK() {
return k;
}
public void setK(K k) {
this.k = k;
}
#Override
public String toString() {
return "Pair [v=" + v + ", k=" + k + "]";
}
}
The full output log:
JSON which represents list of pairs:
[{"school":{"id":1,"schoolId":3,"studentName":"O. Bas"},"student":{"id":3,"schoolName":"School 3"}},{"school":{"id":2,"schoolId":4,"studentName":"C. Koc"},"student":{"id":4,"schoolName":"School 4"}}]
----
Deserialized version:
[{school={id=1, schoolId=3, studentName=O. Bas}, student={id=3, schoolName=School 3}}, {school={id=2, schoolId=4, studentName=C. Koc}, student={id=4, schoolName=School 4}}]
Because the output JSON is not formatted I present it in more understandable version:
[
{
"school":{
"id":1,
"schoolId":3,
"studentName":"O. Bas"
},
"student":{
"id":3,
"schoolName":"School 3"
}
},
{
"school":{
"id":2,
"schoolId":4,
"studentName":"C. Koc"
},
"student":{
"id":4,
"schoolName":"School 4"
}
}
]
As you can see, we create new JsonConverter object with definition of mapping between Pair property names and which names we want to see in JSON string representation. Now if you have for example Pair<School, Room> you can create mapping Map in this way:
private static Map<String, String> createSchoolRoomMapping() {
Map<String, String> mapping = new HashMap<String, String>();
mapping.put("k", "school");
mapping.put("v", "room");
return mapping;
}
I was going for an answer with some annotation (JsonTypeInfo and JsonUnwrapped), but those two don't work well together apparently (see this issue). That would of handled both the serialization and deserialization part of your problem, without relying on custom de/serializer. Instead, you'll need a custom deserializer, which does something along those line:
class PairDeserializer extends JsonDeserializer<Pair>{
static Map<String, Class> MAPPINGS = new HashMap<String, Class>();
#Override
public Pair deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Object key = deserializeField(jp);
Object value = deserializeField(jp);
Pair pair = new Pair();
pair.k = key;
pair.v = value;
jp.nextToken();
return pair;
}
private Object deserializeField(JsonParser jp) throws IOException, JsonParseException, JsonProcessingException {
jp.nextValue();
String className = jp.getCurrentName();
return jp.readValueAs(MAPPINGS.get(className));
}
}
Then you only need to register the mappings you need