I am beginner to core Java.
I am fetching data from sql server using JDBC template and joins.
Now I want to group received data.
processId processName subId subName valueId value gId gradeName
1 p1 1 s1 11 v1 1 g1
1 p1 1 s1 11 v1 2 g2
2 p2 2 s2 null null null null
3 p3 3 s3 13 v3 null null
And I want following output:
[{
"processId": 1,
"processname": "p1",
"sub": [{
"subId": 11,
"subName": "s1",
"value": [{
"valueId": 11,
"value": "v1",
"grades": [{
"gId": 1,
"gradeName": "g1"
}, {
"gId": 2,
"gradeName": "g2"
}]
}]
}]
}, {
"processId": 2,
"processname": "p2",
"sub": [{
"subId": 12,
"subName": "s2",
"value": []
}]
}, {
"processId": 3,
"processname": "p3",
"sub": [{
"subId": 13,
"subName": "s3",
"value": [{
"valueId": 3,
"value": "g3",
"grade": []
}]
}]
}]
I found similar question here: link
I used while loop to iterate over output as mentioned in the linked question, but I am unable to do so.
One of user states in comment that use hashmap But I am not able to implement hashmap. Can anyone guide me about it?
Can anyone guide/help me to sort this out.
There are many ways to do that, one could be the following.
First, create a model to structure your result:
package core.map;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class Process {
private String processId;
private String processName;
#JsonSerialize(using = MapAsListSerializer.class)
private Map<String, Sub> sub = new HashMap<>();
public Process(final String processId, final String processName) {
this.processId = processId;
this.processName = processName;
}
public String getProcessId() {
return processId;
}
public String getProcessName() {
return processName;
}
public Map<String, Sub> getSub() {
return sub;
}
static class Sub {
private String subId;
private String subName;
#JsonSerialize(using = MapAsListSerializer.class)
private Map<String, Value> value = new HashMap<>();
public Sub(final String subId, final String subName) {
this.subId = subId;
this.subName = subName;
}
public String getSubId() {
return subId;
}
public String getSubName() {
return subName;
}
public Map<String, Value> getValue() {
return value;
}
static class Value {
private String valueId;
private String value;
#JsonSerialize(using = MapAsListSerializer.class)
private Map<String, Grade> grades = new HashMap<>();
public Value(final String valueId, final String value) {
this.valueId = valueId;
this.value = value;
}
public String getValueId() {
return valueId;
}
public String getValue() {
return value;
}
public Map<String, Grade> getGrades() {
return grades;
}
static class Grade {
private String gId;
private String gradeName;
public Grade(final String gId, final String gradeName) {
this.gId = gId;
this.gradeName = gradeName;
}
public String getgId() {
return gId;
}
public String getGradeName() {
return gradeName;
}
}
}
}
}
This was just a bunch of (more or less) simple POJOs, except the line:
#JsonSerialize(using = MapAsListSerializer.class)
The MapAsListSerializer is just a tiny custom Json Serializer to display the final result as needed (flat list instead of a map).
package core.map;
import java.io.IOException;
import java.util.Map;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class MapAsListSerializer extends JsonSerializer<Map<?, ?>> {
#Override
public void serialize(Map<?, ?> incomingMap, JsonGenerator generator, SerializerProvider arg2)
throws IOException, JsonProcessingException {
generator.writeObject(incomingMap.values());
}
}
At least we need the code to compute the (fake database) result, this could be something like:
package core.map;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import core.map.Process.Sub;
import core.map.Process.Sub.Value;
import core.map.Process.Sub.Value.Grade;
public class ResultMapper {
private final static ObjectMapper mapper = new ObjectMapper();
private final static List<List<String>> resultSet = new ArrayList<>();
private final static Map<String, Process> processes = new HashMap<>();
static {
resultSet.add(Arrays.asList("1", "p1", "1", "s1", "11", "v1", "1", "g1"));
resultSet.add(Arrays.asList("1", "p1", "1", "s1", "11", "v1", "2", "g2"));
resultSet.add(Arrays.asList("2", "p2", "2", "s2", null, null, null, null));
resultSet.add(Arrays.asList("3", "p3", "3", "s3", "13", "v3", null, null));
}
public static void main(String[] args) throws JsonProcessingException {
resultSet.forEach(row -> rowToProcess(row, processes));
System.out.println(mapper.writeValueAsString(processes.values()));
}
private static void rowToProcess(final List<String> row, final Map<String, Process> processes) {
final String processId = row.get(0);
final String processName = row.get(1);
final String subId = row.get(2);
final String subName = row.get(3);
final String valueId = row.get(4);
final String value = row.get(5);
final String gId = row.get(6);
final String gradeName = row.get(7);
Process currentProcess = processes.get(processId);
if (currentProcess == null) {
currentProcess = new Process(processId, processName);
processes.put(processId, currentProcess);
}
Map<String, Sub> subs = currentProcess.getSub();
Sub currentSub = subs.get(subId);
if (currentSub == null) {
currentSub = new Process.Sub(subId, subName);
subs.put(subId, currentSub);
}
Map<String, Value> values = currentSub.getValue();
if (valueId == null)
return;
Value currentValue = values.get(valueId);
if (currentValue == null) {
currentValue = new Sub.Value(valueId, value);
values.put(valueId, currentValue);
}
if (gId == null)
return;
Map<String, Grade> grades = currentValue.getGrades();
grades.put(gId, new Value.Grade(gId, gradeName));
}
}
So nearly everything is out of the JDK (1.8), except the Json dependencies. All needed extra stuff is available here
Extension:
Of course it's possible, you can omit the getters which are just necessary for Jackson processing. That would result in something like (without getters):
package core.map;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class Process {
private String processId;
private String processName;
#JsonSerialize(using = MapAsListSerializer.class)
private Map<String, Sub> sub = new HashMap<>();
public Process(final String processId, final String processName) {
this.processId = processId;
this.processName = processName;
}
public Map<String, Sub> getSub() {
return sub;
}
static class Sub {
private String subId;
private String subName;
#JsonSerialize(using = MapAsListSerializer.class)
private Map<String, Value> value = new HashMap<>();
public Sub(final String subId, final String subName) {
this.subId = subId;
this.subName = subName;
}
public Map<String, Value> getValue() {
return value;
}
static class Value {
private String valueId;
private String value;
#JsonSerialize(using = MapAsListSerializer.class)
private Map<String, Grade> grades = new HashMap<>();
public Value(final String valueId, final String value) {
this.valueId = valueId;
this.value = value;
}
public Map<String, Grade> getGrades() {
return grades;
}
static class Grade {
private String gId;
private String gradeName;
public Grade(final String gId, final String gradeName) {
this.gId = gId;
this.gradeName = gradeName;
}
}
}
}
}
But then you have to tell Jackson to use the fields directly instead of the getters:
mapper.setVisibility(PropertyAccessor.GETTER, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
I will keep both solutions here because that's something about personal preference. And of course you can omit the Process class in general and write another one similar to that, but somehow the data has to be structured.
Related
The json looks like:
{
"id": "SMAAZGD20R",
"data": [
{
"blukiiId": "CC78AB5E73C8",
"macAddress": "CC78AB5E73C8",
"type": "SENSOR_BEACON",
"battery": 97,
"advInterval": 1000,
"firmware": "003.007",
"rssi": [
{
"rssi": -96,
"timestamp": 1594642177138
}
],
"beaconSensorData": {
"environment": [
{
"airPressure": 994.4,
"light": 5,
"humidity": 26,
"temperature": 28.4,
"timestamp": 1594642177138
}
]
}
}
]
}
The code looks like:
public class getJSON
{
public static void main(String[] args) throws Exception{
JSONParser parser = new JSONParser();
try{
Object obj = parser.parse(new FileReader("C:\\test.json"));
JSONObject jsonObj = (JSONObject)obj;
JSONArray jsonArr = (JSONArray)jsonObj.get("data");
Iterator itr = jsonArr.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}catch(Exception e){
e.printStackTrace();
}
}
}
Output:
{"macAddress":"CC78AB5E73C8","rssi":[{"rssi":-96,"timestamp":1594642177138}],"advInterval":1000,"blukiiId":"CC78AB5E73C8","type":"SENSOR_BEACON","battery":97,"firmware":"003.007","beaconSensorData":{"environment":[{"light":5,"airPressure":994.4,"temperature":28.4,"humidity":26,"timestamp":1594642177138}]}}
I get an Array with the object "data", but the array includes only one value with all objects from "data".
How can i address the array "environment" and get the values tempreature, light,...
Have you should try to use a framework like jackson
Who will let unmarshall your json to real java object of your choice
for example :
public class Data
{
private String blukiiId;
private String macAddress;
private String type;
...
private List<RSSI> rssi;
private BeaconSensorData beaconSensorData;
}
With Rssi,BeaconSensorData another class like that etc...
Now your code will get Converted as below
public class getJSON
{
public static void main(String[] args) throws Exception{
ObjectMapper mapper = new ObjectMapper();
// Set any extra configs like ignore fields etc here
try{
Data data = mapper.convertValue(Files.readAllBytes(Paths.get("test.json"), Data.class);
//Now you can access the value as below
data.getBeaconSensorData().getEnvironment().getTemperature();
}catch(Exception e){
e.printStackTrace();
}
}
}
If you can only use org.json.simple.parser.JSONParser, please refer to the following java code by recursion.
import java.io.FileReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
public class getJSON
{
private static void visitElement(Object obj, Map<Object, Object> map) throws Exception {
if(obj instanceof JSONArray) {
JSONArray jsonArr = (JSONArray)obj;
Iterator<?> itr = jsonArr.iterator();
while(itr.hasNext())
visitElement(itr.next(), map);
}
else if(obj instanceof JSONObject) {
JSONObject jsonObj = (JSONObject)obj;
Iterator<?> itr = jsonObj.keySet().iterator();
while(itr.hasNext()) {
Object key = itr.next();
Object value = jsonObj.get(key);
map.put(key, value);
visitElement(value, map);
}
}
}
public static void main(String[] args) throws Exception{
JSONParser parser = new JSONParser();
try{
Object obj = parser.parse(new FileReader("C:/test.json"));
Map<Object, Object> map = new HashMap<>();
visitElement(obj, map);
for(Object key : map.keySet())
System.out.println(key + ": " + map.get(key));
}catch(Exception e){
e.printStackTrace();
}
}
}
Look at my code below.
[
{
"title": "Dywizjon 303",
"year": 2019,
"type": "Dokumentalny",
"director": "Zbigniew Zbigniew",
"actors": ["Maria Joao","Jose Raposo"]
},
{
"title": "Szeregowiec Ryan",
"year": 2006,
"type": "Historyczny",
"director": "Stanislaw Stanislaw",
"actors": ["Rosa Canto","Amalia Reis","Maria Garcia"]
},
{
"title": "Duzy",
"year": 1988,
"type": "Dramat",
"director": "Penny Marshall",
"actors": ["Rosa Canto"]
},
{
"title": "Syberiada Polska",
"year": 2013,
"type": "Wojenny",
"director": "Janusz Zaorski",
"actors": ["Harvey Glazer"]
}
]
This is my Entity named Movie
package objectMapper.app;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
#JacksonXmlRootElement(localName="Class")
public class Movie {
#JacksonXmlProperty(localName="title")
private String title;
#JacksonXmlProperty(localName="year")
private int year;
#JacksonXmlProperty(localName="type")
private String type;
#JacksonXmlProperty(localName="director")
private String director;
#JacksonXmlProperty(localName="actors")
private String[] actors;
public Movie()
{
}
public Movie(String title, int year, String type, String director, String[] actors) {
this.title = title;
this.year = year;
this.type = type;
this.director = director;
this.actors = actors;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String[] getActors() {
return actors;
}
public void setActors(String[] actors) {
this.actors = actors;
}
#Override
public String toString() {
return "Movie [title=" + title + ", year=" + year + ", type=" + type + ", director=" + director + ", actors="
+ Arrays.toString(actors) + "]";
}
}
Function to read movies.json
public Movie[] readJSONFile() throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper= new ObjectMapper();
Movie[] jsonObj=mapper.readValue(new File("movies.json"),Movie[].class);
return jsonObj;
}
Lets do something with our POJO class
List<String> titles=new ArrayList();
for(Movie itr: tempMovies)
{
titles.add(itr.getTitle().toLowerCase());
}
if(titles.contains(tempTitle.toLowerCase()))
{
for(Movie itr2 : tempMovies)
{
if(tempTitle.toLowerCase().equals(itr2.getTitle().toLowerCase()))
{
System.out.println("Title: "+itr2.getTitle());
System.out.println("Year: "+itr2.getYear());
System.out.println("Type: "+itr2.getType());
System.out.println("Director: "+itr2.getDirector());
String[] tempActorsStrings=itr2.getActors();
int size=tempActorsStrings.length;
for(int i=0;i<size;i++)
{
System.out.println("Actor: "+tempActorsStrings[i]);
}
status=false;
}
}
}
else
{
System.out.println("Movie title does not exist!");
}
my enum:
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Currency {
USD("USD", "United States Dollar"),
EUR("EUR", "Euro"),
BGP("BGP", "British Pound"),
AUD("AUD", "Australian Dollar"),
CAD("CAD", "Canadian Dollar");
private final String shortName;
private final String fullName;
private Map<Enum, Double> rates;
Currency(String shortName, String fullName) {
this.shortName = shortName;
this.fullName = fullName;
this.rates = new HashMap<>();
}
public String getShortName() {
return shortName;
}
public String getFullName() {
return fullName;
}
public Map<Enum, Double> getRates() {
return rates;
}
}
Postman response I get from another rest api:
{
"shortName": "EUR",
"fullName": "Euro",
"rates": {
"AUD": 1.62,
"CAD": 1.47,
"USD": 1.11,
"BGP": 0.86,
"EUR": 1.0
}
}
Title pretty much sums up what I need. Any ideas how to serialize the postman response in my code, so i have enum as a result, which contains all properties, including "rates" map?
Thanks in advance.
You need to create a static factory method annotated with com.fasterxml.jackson.annotation.JsonCreator annotation. In case when whole JSON Object represents enum, Jackson automatically converts it to Map, so this method should have signature like below:
#JsonCreator
public static Currency from(Map<String, Object> value)
Below showcase provides complete implementation:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./src/main/resources/test.json");
ObjectMapper mapper = new ObjectMapper();
Currency currency = mapper.readValue(jsonFile, Currency.class);
System.out.println(currency + " => " + currency.getRates());
}
}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
enum Currency {
USD("USD", "United States Dollar"),
EUR("EUR", "Euro"),
BGP("BGP", "British Pound"),
AUD("AUD", "Australian Dollar"),
CAD("CAD", "Canadian Dollar");
private final String shortName;
private final String fullName;
private Map<Enum, Double> rates;
Currency(String shortName, String fullName) {
this.shortName = shortName;
this.fullName = fullName;
this.rates = Collections.emptyMap();
}
public String getShortName() {
return shortName;
}
public String getFullName() {
return fullName;
}
public Map<Enum, Double> getRates() {
return rates;
}
public static Currency fromShortName(String value) {
for (Currency currency : values()) {
if (currency.getShortName().equals(value)) {
return currency;
}
}
throw new IllegalArgumentException(value + " is not found!");
}
#JsonCreator
public static Currency from(Map<String, Object> value) {
String shortName = value.getOrDefault("shortName", "").toString();
Currency currency = fromShortName(shortName);
Map<String, Double> rates = (Map<String, Double>) value.getOrDefault("rates", Collections.emptyMap());
Map<Enum, Double> newRates = new HashMap<>(9);
rates.forEach((k, v) -> {
newRates.put(Currency.fromShortName(k), v);
});
currency.rates = Collections.unmodifiableMap(newRates);
return currency;
}
}
Above code prints:
EUR => {EUR=1.0, AUD=1.62, CAD=1.47, USD=1.11, BGP=0.86}
Warning
enum should be an Immutable object and keeping rates Map inside is not a good idea. You should definitely try to remove it from enum. In multi thread environment you need to guarantee that this Map will not be change during the read. So, in implementation I do not change already used Map but create new every time.
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 created a json which have a root node with couple of child nodes using java now i have a requirement that the child node under the root may also have some children.But i am unable to do that.Here is what i have done so far....
class Entry {
private String name;
public String getChildren() {
return name;
}
public void setChildren(String name) {
this.name = name;
}
}
public class JsonApplication {
public static void main(String[] args) {
// TODO code application logic here
String arr[] = {"Culture", "Salary", "Work", "Effort"};
EntryListContainer entryListContainer = new EntryListContainer();
List<Entry> entryList1 = new ArrayList<>();
List<Entry> entryList2 = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
Entry entry1 = new Entry();
entry1.setChildren(arr[i]);
entryList1.add(entry1);
entryList2.add(entry1);
/*Child nodes are created here and put into entryListContainer*/
entryListContainer.setEntryList1(entryList1);
entryListContainer.setEntryList1(entryList2);
}
/*Root node this will collapse and get back to Original position on click*/
entryListContainer.setName("Employee");
entryListContainer.setName("Culture");
Map<String, String> mapping = new HashMap<>();
mapping.put("entryList1", "name");
Gson gson = new GsonBuilder().serializeNulls().setFieldNamingStrategy(new DynamicFieldNamingStrategy(mapping)).create();
System.out.println(gson.toJson(entryListContainer));
}
}
class DynamicFieldNamingStrategy implements FieldNamingStrategy {
private Map<String, String> mapping;
public DynamicFieldNamingStrategy(Map<String, String> mapping) {
this.mapping = mapping;
}
#Override
public String translateName(Field field) {
String newName = mapping.get(field.getName());
if (newName != null) {
return newName;
}
return field.getName();
}
}
class EntryListContainer {
private List<Entry> children;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setEntryList1(List<Entry> entryList1) {
this.children = entryList1;
}
public List<Entry> getEntryList1() {
return children;
}
}
This is the json output i am getting
{
"children": [
{
"name":"Culture"
},
{
"name":"Salary"
},
{
"name":"Work"
},
{
"name":"Effort"
}
],
"name":"Employee"
}
But i need
{
"name":"Culture",
"children":[
{
"name":"Culture"
},
{
"name":"Salary"
},
{
"name":"Work"
},
{
"name":"Effort"
}
],
"name":"Work",
"children" : [
{
"name":"Culture"
},
{
"name":"Work"
}
]
}
I'm a bit confused by your code, but something is clear to me: what you want to get. So starting from scratch I created some code you can copy&run to see how you can get your desired JSON.
Probably the order of elements is important for you (pay attention that in JSON object order of keys is not important -is a map!-), so I edited some code that is not pure Gson way of doing things but that creates exactly your example.
package stackoverflow.questions;
import java.lang.reflect.Field;
import java.util.*;
import com.google.gson.*;
public class JsonApplication {
public static class EntryListContainer {
public List<Entry> children = new ArrayList<Entry>();
public Entry name;
}
public static class Entry {
private String name;
public Entry(String name) {
this.name = name;
}
}
public static void main(String[] args) {
EntryListContainer elc1 = new EntryListContainer();
elc1.name = new Entry("Culture");
elc1.children.add(new Entry("Salary"));
elc1.children.add(new Entry("Work"));
elc1.children.add(new Entry("Effort"));
EntryListContainer elc2 = new EntryListContainer();
elc2.name = new Entry("Work");
elc2.children.add(new Entry("Culture"));
elc2.children.add(new Entry("Work"));
ArrayList<EntryListContainer> al = new ArrayList<EntryListContainer>();
Gson g = new Gson();
al.add(elc1);
al.add(elc2);
StringBuilder sb = new StringBuilder("{");
for (EntryListContainer elc : al) {
sb.append(g.toJson(elc.name).replace("{", "").replace("}", ""));
sb.append(",");
sb.append(g.toJson(elc.children));
sb.append(",");
}
String partialJson = sb.toString();
if (al.size() > 1) {
int c = partialJson.lastIndexOf(",");
partialJson = partialJson.substring(0, c);
}
String finalJson = partialJson + "}";
System.out.println(finalJson);
}
}
and this is the execution:
{"name":"Culture",[{"name":"Salary"},{"name":"Work"},{"name":"Effort"}],"name":"Work",[{"name":"Culture"},{"name":"Work"}]}
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