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.
Related
I would like create a Java object which map this JSON object:
{
"base_currency_code": "HKD",
"base_currency_name": "Hong Kong dollar",
"amount": "150.5800",
"updated_date": "2022-03-20",
"rates": {
"GBP": {
"currency_name": "Pound sterling",
"rate": "0.0975",
"rate_for_amount": "14.6774"
}
},
"status": "success"
}
Only the "GBP" property name is dynamic field, it could be another currency symbol next time like "USD", "JPY" etc.
I create the Java class like this:
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"base_currency_code",
"base_currency_name",
"amount",
"updated_date",
"rates",
"status"
})
public class CurrencyConvertDto {
#JsonProperty("base_currency_code")
private String baseCurrencyCode;
#JsonProperty("base_currency_name")
private String baseCurrencyName;
#JsonProperty("amount")
private String amount;
#JsonProperty("updated_date")
private String updatedDate;
#JsonProperty("rates")
private Rates rates;
#JsonProperty("status")
private String status;
/*
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
*/
#JsonProperty("base_currency_code")
public String getBaseCurrencyCode() {
return baseCurrencyCode;
}
#JsonProperty("base_currency_code")
public void setBaseCurrencyCode(String baseCurrencyCode) {
this.baseCurrencyCode = baseCurrencyCode;
}
#JsonProperty("base_currency_name")
public String getBaseCurrencyName() {
return baseCurrencyName;
}
#JsonProperty("base_currency_name")
public void setBaseCurrencyName(String baseCurrencyName) {
this.baseCurrencyName = baseCurrencyName;
}
#JsonProperty("amount")
public String getAmount() {
return amount;
}
#JsonProperty("amount")
public void setAmount(String amount) {
this.amount = amount;
}
#JsonProperty("updated_date")
public String getUpdatedDate() {
return updatedDate;
}
#JsonProperty("updated_date")
public void setUpdatedDate(String updatedDate) {
this.updatedDate = updatedDate;
}
#JsonProperty("rates")
public Rates getRates() {
return rates;
}
#JsonProperty("rates")
public void setRates(Rates rates) {
this.rates = rates;
}
#JsonProperty("status")
public String getStatus() {
return status;
}
#JsonProperty("status")
public void setStatus(String status) {
this.status = status;
}
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Rates {
private List<Map<String, String>> rateInfo = new ArrayList<Map<String, String>>();
#JsonAnySetter
public void setDynamicProperty(String name, Map<String, String> map) {
rateInfo.add(map);
}
public List<Map<String, String>> getRateInfo() {
return rateInfo;
}
public void setRateInfo(List<Map<String, String>> rateInfo) {
this.rateInfo = rateInfo;
}
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"currency_name",
"rate",
"rate_for_amount"
})
public class RateInfo {
#JsonProperty("currency_name")
private String currencyName;
#JsonProperty("rate")
private String rate;
#JsonProperty("rate_for_amount")
private String rateForAmount;
/*
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
*/
#JsonProperty("currency_name")
public String getCurrencyName() {
return currencyName;
}
#JsonProperty("currency_name")
public void setCurrencyName(String currencyName) {
this.currencyName = currencyName;
}
#JsonProperty("rate")
public String getRate() {
return rate;
}
#JsonProperty("rate")
public void setRate(String rate) {
this.rate = rate;
}
#JsonProperty("rate_for_amount")
public String getRateForAmount() {
return rateForAmount;
}
#JsonProperty("rate_for_amount")
public void setRateForAmount(String rateForAmount) {
this.rateForAmount = rateForAmount;
}
}
But when compile, it seems have problem. it can't map the JSON object with the dynamic field . Does anyone know how to fix it? Thanks you very much.
You can use #JsonAnyGetter and get the additional dynamic key value pair(s) in a Map. Please refer to the usage of #JsonAnyGetter in the following example and do let me know if you still need help.
https://www.logicbig.com/tutorials/misc/jackson/jackson-any-setter.html
If you are able,
the simplest option would be to use a reasonable JSON design.
Here is an example:
{
"base_currency_code": "HKD",
"base_currency_name": "Hong Kong dollar",
"amount": "150.5800",
"updated_date": "2022-03-20",
"rates": {
"currentySymbol": "GBP",
"currency_name": "Pound sterling",
"rate": "0.0975",
"rate_for_amount": "14.6774"
}
},
"status": "success"
}
May be you were thinking too complicated here.
In your CurrencyConvertDto class, instead of using
#JsonProperty("rates")
private Rates rates;
you can simply use
#JsonProperty("rates")
private Map<String, RateInfo> rates;
(and of course adjust the getRatesand setRates methods accordingly).
And then you don't need the Rates class anymore.
Jackson can cope with this out-of-the-box.
It will handle arbitrary currency codes as keys of the map, like in:
{
"base_currency_code": "HKD",
"base_currency_name": "Hong Kong dollar",
"amount": "150.5800",
"updated_date": "2022-03-20",
"rates": {
"GBP": {
"currency_name": "Pound sterling",
"rate": "0.0975",
"rate_for_amount": "14.6774"
},
"EUR": {
"currency_name": "Euro",
"rate": "0.120",
"rate_for_amount": "18.07"
}
},
"status": "success"
}
And by the way: You don't need to repeat the #JsonProperty
annotations on the getter and setter methods. Putting #JsonProperty
only on the member variables is already enough.
I have the following json String:
"{\"rates\":{\"CAD\":1.5601,\"HKD\":8.4781,\"ISK\":156.1,\"PHP\":55.709,\"DKK\":7.4642,\"HUF\":369.36,\"CZK\":27.369,\"AUD\":1.8053,\"RON\":4.834,\"SEK\":10.9368,\"IDR\":18239.61,\"INR\":83.6004,\"BRL\":5.7349,\"RUB\":86.475,\"HRK\":7.6285,\"JPY\":117.55,\"THB\":36.111,\"CHF\":1.0564,\"SGD\":1.5689,\"PLN\":4.5815,\"BGN\":1.9558,\"TRY\":7.2925,\"CNY\":7.7653,\"NOK\":11.2685,\"NZD\":1.8547,\"ZAR\":19.6619,\"USD\":1.0936,\"MXN\":26.4097,\"ILS\":3.9015,\"GBP\":0.8846,\"KRW\":1346.48,\"MYR\":4.7654},\"base\":\"EUR\",\"date\":\"2020-04-01\"}"
it's a list of rates with the matching rate values, these have been retrieved from here.
What I want to do, is to deserialize the string into an object, that I've already created
package com.example.android.myrates.core.json;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class JsonLatestStructure {
#SerializedName("rates")
#Expose
private JsonRatesStructure rates;
#SerializedName("base")
#Expose
private String base;
#SerializedName("date")
#Expose
private String date;
public JsonRatesStructure getRates() {
return rates;
}
public void setRates(JsonRatesStructure rates) {
this.rates = rates;
}
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
I currently have a method that creates a new Gson object, and I want to be able to get all the rate names and matching rate values into a hashmap.
JsonLatestStructure jsonData = new Gson().fromJson(rateListPresenter.getLatestRateList(), JsonLatestStructure.class);
Thanks in advance!
UPDATE:
I accomplished what I wanted by doing this:
JsonLatestStructure jsonData = new Gson().fromJson(rateListPresenter.getLatestRateList(), JsonLatestStructure.class);
Map<String, Double> rateListItems = jsonData.getRates();
Try below it will return all dynamic key and value list
JSONObject data = jsonResponse.getJSONObject("rates");// here response is server response
Iterator keys = data.keys();
while(keys.hasNext()) {
// loop to get the dynamic key
String key = (String)keys.next(); // it returns a key ;ile CAD, HKD etc...
// get the value of the dynamic key
int value = data.getInt(key); // it returns a value like 1.5601,8.4781 etc...
}
I made a Map to store the values of all tags on a SOAPMessage body, so that the keys of the Map are the node names and the values are the text contents. I have an object where I already have the fields named after the node names, what I need to do is to set their values accordingly to their map counterparts.
Say that I have a node named "Summary" on the SOAPMessage, there will be a Map key named "Summary" and also an object field named "Summary". I need the object field "Summary" to be set as the value of the Map.get("Summary").
I know I could just fill my code with setters for each of the fields, but is there an way to set the entire object from the Map?
This is the method where I created the Map.
private static Map<String, String> mapIncidentInfo(SOAPMessage soapResponse) throws SOAPException {
Map<String, String> fields = new HashMap<String, String>();
NodeList nodes = soapResponse.getSOAPBody().getFirstChild().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
fields.put(node.getNodeName(), node.getTextContent());
}
return fields;
}
This could be used as an object class example:
public class IncidentPO {
private String Submitter;
private String Submit_Date;
private String Last_Modified_By;
private String Last_Modified_Date;
private String Status;
private String Short_Description;
public IncidentPO(String Submitter, String Submit_Date, String Last_Modified_By, String Last_Modified_Date, String Status, String Short_Description) {
this.Submitter = Submitter;
this.Submit_Date = Submit_Date;
this.Last_Modified_By = Last_Modified_By;
this.Last_Modified_Date = Last_Modified_Date;
this.Status = Status;
this.Short_Description = Short_Description;
//getters and setters here
There's no easy way (without libraries) to convert a Map to object. A direct option is to provide the Map in a constructor and have it populate itself.
public IncidentPO(Map<String, String> map) {
this.Submitter = map.get("Submitter");
this.Submit_Date = map.get("Submit_Date");
// etc
}
You can use object to json mapping then again json to object as below:
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class TestMap {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
Map<String, String> myMmap = new HashMap<>();
myMmap.put("name", "ABC");
myMmap.put("age", "20");
myMmap.put("sex", "male");
myMmap.put("city", "madhepura");
myMmap.put("spec", "java");
ObjectMapper mapper = new ObjectMapper();
ObjectNode objectNode1 = mapper.createObjectNode();
for(String key : myMmap.keySet()) {
objectNode1.put(key, myMmap.get(key));
}
// take the value of objectNode1.toString() and create a pojo from http://www.jsonschema2pojo.org/
Person person = mapper.readValue(objectNode1.toString().getBytes(), Person.class);
System.out.println(person);
}
}
// you can use http://www.jsonschema2pojo.org to get POJO from objectNode1.toString()
// {"city":"patna","sex":"male","name":"ABC","age":"20","spec":"java"}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"city",
"sex",
"name",
"age",
"spec"
})
class Person {
#JsonProperty("city")
private String city;
#JsonProperty("sex")
private String sex;
#JsonProperty("name")
private String name;
#JsonProperty("age")
private String age;
#JsonProperty("spec")
private String spec;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("city")
public String getCity() {
return city;
}
#JsonProperty("city")
public void setCity(String city) {
this.city = city;
}
#JsonProperty("sex")
public String getSex() {
return sex;
}
#JsonProperty("sex")
public void setSex(String sex) {
this.sex = sex;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("age")
public String getAge() {
return age;
}
#JsonProperty("age")
public void setAge(String age) {
this.age = age;
}
#JsonProperty("spec")
public String getSpec() {
return spec;
}
#JsonProperty("spec")
public void setSpec(String spec) {
this.spec = spec;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
I'm having difficulty trying to parse this JSON response into a list of "properties" elements. My JSON looks like this:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"mag": 6.6,
"place": "192km ESE of Tadine, New Caledonia"
}
},
{
"type": "Feature",
"properties": {
"mag": 7.5,
"place": "168km ESE of Tadine, New Caledonia"
}
},
{
"type": "Feature",
"properties": {
"mag": 6,
"place": "155km ESE of Tadine, New Caledonia"
}
}
]
}
This is response contains Earthquake details so basically each "properties" within "features" is the POJO I want, but all of them just in a List. Here is my Earthquake class:
public class Earthquake {
#SerializedName("mag")
private double magnitude;
#SerializedName("place")
private String location;
public Earthquake(double magnitude, String location) {
this.magnitude = magnitude;
this.location = location;
}
// getters
}
I've tried doing custom deserialization suggested here. It gives me the error
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
suggesting that I'm trying to parse a JsonObject instead of a JsonArray. Here is the deserializer I used.
public class EarthquakeDeserializer implements JsonDeserializer<ArrayList<Earthquake>> {
#Override
public ArrayList<Earthquake> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// get list of "features"
JsonElement features = json.getAsJsonObject().get("features");
JsonArray earthquakeElements = new JsonArray();
for (JsonElement feature : features.getAsJsonArray()){
JsonElement properties = feature.getAsJsonObject().get("properties");
earthquakeElements.add(properties);
}
Type listType = new TypeToken<ArrayList<Earthquake>>(){}.getType();
return new Gson().fromJson(earthquakeElements, listType);
}
}
Any ideas as to what's going on here?
you can create this kind of a POJO class for your Json, No matter if you want just single part of your response body, you need to create POJO for whole response and from that POJO you need to get appropriate attributes. ->
This is your main json object ->
public class Example {
#SerializedName("type")
#Expose
private String type;
#SerializedName("features")
#Expose
private List<Feature> features = null;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Feature> getFeatures() {
return features;
}
public void setFeatures(List<Feature> features) {
this.features = features;
}
}
this is your feature class ->
package com.example;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Feature {
#SerializedName("type")
#Expose
private String type;
#SerializedName("properties")
#Expose
private Properties properties;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
this is your properties class ->
package com.example;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Properties {
#SerializedName("mag")
#Expose
private Integer mag;
#SerializedName("place")
#Expose
private String place;
public Integer getMag() {
return mag;
}
public void setMag(Integer mag) {
this.mag = mag;
}
public String getPlace() {
return place;
}
public void setPlace(String place) {
this.place = place;
}
}
After creating this classes, you can serialize the JSON to POJO via GSON library, you can refer to HussainAbbas's answer for how to do it.
Now you can get anything via creating object of response class, and via that object you can access any property you want. Thanks.
check this out
package com.example;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Example {
#SerializedName("type")
#Expose
private String type;
#SerializedName("features")
#Expose
private List<Feature> features = null;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Feature> getFeatures() {
return features;
}
public void setFeatures(List<Feature> features) {
this.features = features;
}
}
-----------------------------------com.example.Feature.java-----------------------------------
package com.example;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Feature {
#SerializedName("type")
#Expose
private String type;
#SerializedName("properties")
#Expose
private Properties properties;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
-----------------------------------com.example.Properties.java-----------------------------------
package com.example;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Properties {
#SerializedName("mag")
#Expose
private Integer mag;
#SerializedName("place")
#Expose
private String place;
public Integer getMag() {
return mag;
}
public void setMag(Integer mag) {
this.mag = mag;
}
public String getPlace() {
return place;
}
public void setPlace(String place) {
this.place = place;
}
}
After this add this... in your retrofit response
Gson gson = new Gson()
String data = gson.toJson(response.body());
LoginResponse loginResponse = gson.fromJson(dataString,LoginResponse.class);
Example example = gson.fromJson(dataString,Example.class);
String feature_proprerties_mag = example.getFeatures().getProperties().getMag
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.