Include classname as a wrapper object in json - java

I am currently building a JSON in java that will be used as a requestbody when calling a POST-endpoint. The class I need to build into a JSON looks like this:
public class EmployeeImport {
#SerializedName("Header")
private final Header header;
#SerializedName("Employees")
private final List<Employee> employees;
public EmployeeImport(final Header header,
final List<Employee> employees) {
this.header = header;
this.employees = employees;
}
I use GSON to produce a JSON from this class:
private String generateJSONForImport(final Employee employee, final Header header) {
final EmployeeImport employeeImport = new EmployeeImport(header, Arrays.asList(employee));
final Gson gson = new Gson();
final String json = gson.toJson(employeeImport);
System.out.println(json);
}
When printing the JSON, it looks like this:
{
"Header": {
"Date": "2021-01-14"
},
"Employees": [
{
"ValueOne": "lala",
"ValueTwo": "lalala"
}
]
}
But I need the JSON to include the class "EmployeeImport" as a wrapping object, like this:
{
"EmployeeImport": {
"Header": {
"Date": "2021-01-13"
},
"Employees": [{
"ValueOne": "lala",
"ValueTwo": "lalala"
}]
}
}
I've searched for a good solution for this, and I have seen that the annotation "#JsonTypeInfo" seems to fix this, but this seems to only work for the jackson-library.
Does anyone know a good solution for how this could be done with the GSON-library?

You can create that wrapper class - and use that as your EmployeeImport.
In the below example I converted your existing EmployeeImport class into an inner class called EmployeeImportInner - and placed it inside EmployeeImport:
public class EmployeeImport {
#SerializedName("EmployeeImport")
private EmployeeImportInner inner;
public class EmployeeImportInner {
#SerializedName("Header")
private Header header;
#SerializedName("Employees")
private List<Employee> employees;
public EmployeeImportInner(final Header header,
final List<Employee> employees) {
this.header = header;
this.employees = employees;
}
// getters/setters not shown
}
// getter/setter not shown
}
You don't have to do it this way - you can have 2 separate classes, if you prefer.
In my case, I build the outer object like this:
EmployeeImport employeeImport = new EmployeeImport();
final EmployeeImport.EmployeeImportInner employeeImportInner =
employeeImport.new EmployeeImportInner(header, employees);
employeeImport.setInner(employeeImportInner);
Now you will see that outer object name you require in your JSON.

Related

Gson return null for the nested-object

Actually, I try to implement so basic thing on my code. I have a json file. Then I want to read this json file and convert it to a Java object. To handle it, I used gson but somehow it returns null for nested-object.
Json:
{
"results": {
"ads-result": {
"data": [
{
"id": "1"
}
]
}
}
}
TestJson:
#Getter
public class TestJson
{
private ResultsData results;
#Getter
public static class ResultsData
{
#JsonProperty(value = "ads-result")
private AdsResultData adsResult;
}
#Getter
public static class AdsResultData
{
private List<AdApp> data;
}
#Getter
public static class AdApp
{
private long id;
}
}
and finally I try to read it from the json file:
public static TestJson getDefaultAdResult() throws Exception
{
String json = IOUtils.toString(
getResourceAsStream("test.json"), StandardCharsets.UTF_8);
return new Gson().fromJson(json, TestJson.class);
}
But at the end of it, when I try to reach the System.out.println(resultJson.getResults().getAdsResult()); it gives null. Any suggestion?
You are mixing Jackson and Gson annotations.
Make this change:
// #JsonProperty(value = "ads-result") - Jackson
#SerializedName("ads-result") // Gson
private AdsResultData adsResult;

JSON to Java object with Random Key

I am trying to convert following JSON to Java object and ending up with UnrecognizedPropertyException.
{
"5214": [{
"name": "sdsds",
"age": "25",
"address": null
},
{
"name": "sdfds",
"age": "26",
"address": null
}]
}
Here "5214" is the random key that I get. I can covert it by modifying JSON little bit. But I want to know whether any possible way to convert the mentioned JSON. I even tried with following snippet taking some reference.
public class SampleTest {
private Map<String, List<EmployeeDetails>> employeeDetails = new HashMap<String, List<EmployeeDetails>>();
public Map<String, List<EmployeeDetails>> getEmployeeDetails() {
return employeeDetails;
}
public void setEmployeeDetails(Map<String, List<EmployeeDetails>> employeeDetails) {
this.employeeDetails = employeeDetails;
}
}
public class EmployeeDetails {
private String name;
private String age;
private String address;
//Getters and Setters
}
Can someone guide me on this?
Use Type Reference (Import Jackson Package for Java)
TypeReference<Map<String, List<EmployeeDetails>>> typeReference = new TypeReference<Map<String, List<EmployeeDetails>>>()
{
};
Map<String, List<EmployeeDetails>> employeeDetails = new ObjectMapper().readValue(jsonString, typeReference);
Check something from that
Maybe:
public class Data {
// String contain the Key, for example: 5214
Map<String, List<EmployeeDetails>> employeeDetails =
new HashMap<String,List<EmployeeDetails>>();
public Data() {
}
#JsonAnyGetter
public Map<String, List<EmployeeDetails>> getEmployeeDetails() {
return employeeDetails;
}
}
I would use custom deserializer with few helper classes. To make the code (matter of opinion I guess) clearer, create the list object:
#SuppressWarnings("serial")
#Getter #Setter
public class EmployeeDetailsList extends ArrayList<EmployeeDetails> {
// this will hold the arbitrary name of list. like 5214
private String name;
}
Then this list seems to be inside an object, say Wrapper:
#Getter
#RequiredArgsConstructor
#JsonDeserialize(using = WrapperDeserializer.class)
public class Wrapper {
private final EmployeeDetailsList employeeDetailsList;
}
So there is annotation #JsonDeserializer that handles deserializing Wrapper. It is not possible to directly deserialize unknown field names to some defined type so we need to use mechanism like this custom deserializer that inspects what is inside Wrapper and determines what to deserialize and how.
And here is how the deserializer works:
public class WrapperDeserializer extends JsonDeserializer<Wrapper> {
private final ObjectMapper om = new ObjectMapper();
#Override
public Wrapper deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
TreeNode node = p.readValueAsTree();
// This is the place for caution. You should somehow know what is the correct node
// Here I happily assume there is just the one and first
String fName = node.fieldNames().next();
EmployeeDetailsList edl = om.readValue(node.get(fName).toString(),
EmployeeDetailsList.class);
edl.setName(fName);
return new Wrapper(edl);
}
}
Please check it carefully it is not perfect in sense finding alwasy the correct node and maybe the instantiation can be done in other ways better. But it shoudl give you a hunch how it could be done.

Mapping JSON response to Java POJO Class using GSON

I am trying to map below JSON to a POJO Class using Gson library. Below is the JSON response and POJO Class and mapping done
import java.util.Map;
import com.google.gson.JsonElement;
public class DataResponse {
private String $status;
private Map<String, JsonElement> $payload;
public String get$status() {
return $status;
}
public void set$status(String $status) {
this.$status = $status;
}
public Map<String, JsonElement> get$payload() {
return $payload;
}
public void set$payload(Map<String, JsonElement> $payload) {
this.$payload = $payload;
}
}
Here is the Sample JSON.
{
"$status": "OK",
"$payload": {
"$nextStart": "123",
"$results": [
{
"$key": "101",
"score": 3,
"to": "Test1"
},
{
"$key": "102",
"score": 4,
"to": "Test2"
},
]
}
}
Below is the mapping done. Is there some problem with POJO class definition. Since I cannot get all the elements of JSON response mapped to the innermost element from the response. Appreciate your support in providing useful suggestions.
Gson gson = new Gson();
DataResponse dataResponse = gson.fromJson(EntityUtils.toString(response.getEntity()),
DataResponse.class);
While working with marshalling and unmarshalling, it is always good to have a model defined as:
public class DataResponse {
private String $status;
private Payload $payload;
// getters and setters
}
class Payload {
private String $nextStart;
private List<Result> $results;
// getters and setters
}
class Result {
private String $key;
private String score;
private String to;
// getters and setters
}
Now when you convert json to POJO as:
Gson gson = new Gson();
DataResponse dataResponse = gson.fromJson(EntityUtils.toString(response.getEntity()), DataResponse.class);
it can easily convert it.
Also, believe me, it is good for processing in your further code!
Update: if you really want to convert json to Map, then you can do something like this:
import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;
Type type = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> myMap = gson.fromJson("{'key':'value'}", type);
Substitute json string there.

Jackson JSON Convert JSON Object into HashMap

I'm starting using jackson json after Jaunt, but i can't understand some details in deserialization.
I have JSON response from server, here it is:
{
success: true,
stickers: [
{
id: "88899",
name: "",
img: ""
},
{
id: "13161",
name: "3DMAX",
img: ""
}
]
}
I have a Sticker class in my project
public class Sticker {
#JsonProperty("id")
private String id;
#JsonProperty("name")
private String name;
#JsonProperty("img")
private String imgUrl;
//getters and setters and constructors
}
I want to read this response and create a List of Stickers:
List(Sticker) stickerList;
**Another task (if it is possible with jackson tools), i want to create HashMap(String, Sticker) during deserilization instead of List(Sticker) **
How to do it easy and properly?
I have found a solution for List: I created class
public class StickerPage {
#JsonProperty("stickers")
private List<Sticker> stickerList;
public List<Sticker> getStickerList() {
return stickerList;
}
public void setStickerList(List<Sticker> stickerList) {
this.stickerList = stickerList;
}
}
And used it for this:
ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
StickerPage stickerPage = objectMapper.readValue(inputStream, StickerPage.class);
HashMap<String, Sticker> stickerHashMap = new HashMap<>();
for (Sticker sticker : stickerPage.getStickerList()) {
stickerHashMap.put(sticker.getName(), sticker);
}
But it looks weird i think, can you help me with this task?

Jackson deserialization - aggregate several fields

suppose I've got a collection of people defined like this in JSON.
{
"NOM": "Doe",
"PRENOM": "John",
"EMAIL": "john.doe#email.me",
"VILLE": "Somewhere",
"LIKE1": "Lolcats",
"LIKE2": "Loldogs",
"LIKE3": "Lolwut",
"HATE1": "Bad stuff",
"HATE2": "Bad bad stuff"
}
Is it possible to write a JsonDeserializer that will aggregate and transform LIKE* and HATE* fields into a collection of Liking, set as a property of Person? (Note that there are only LIKE1, LIKE2, LIKE3, HATE1, HATE2.)
The final result properties would be something like:
public class Person {
private final String lastName;
private final String firstName;
private final String email;
private final String town;
private final Collection<Liking> likings;
// c-tor, getters
}
I've already the logic that can deserialize a given LIKE*/HATE* property into a Liking object but I fail to understand to aggregate and add them to a Person liking attribute.
Thx in advance!
It would have been nice if you had some code that showed you began the process of solving this problem yourself. But, here is a sample custom deserializer that does pretty much what you're looking for:
class PersonDeserializer extends JsonDeserializer<Person> {
#Override
public Person deserialize(final JsonParser parser,
final DeserializationContext content) throws IOException,
JsonProcessingException {
final ObjectCodec codec = parser.getCodec();
final JsonNode node = codec.readTree(parser);
final Person person = new Person();
final Iterator<String> fieldNameIter = node.getFieldNames();
while (fieldNameIter.hasNext()) {
final String fieldName = fieldNameIter.next();
if (fieldName.equalsIgnoreCase("EMAIL")) {
person.setEmail(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("NOM")) {
person.setFirstName(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("PRENOM")) {
person.setLastName(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("VILLE")) {
person.setTown(node.get(fieldName).getTextValue());
} else if (fieldName.startsWith("LIKE")) {
person.addLike(Liking.LikingType.LIKE, node.get(fieldName)
.getTextValue());
} else if (fieldName.startsWith("HATE")) {
person.addLike(Liking.LikingType.HATE, node.get(fieldName)
.getTextValue());
}
}
return person;
}
}
It presumes a Liking object similar to this:
public class Liking {
public static enum LikingType {
LIKE, HATE;
}
private LikingType type;
private String value;
// Constructors, getters/setters
}
And some changes to your Person object which I think you can figure out. If you intend to serialize the object to JSON in the same custom format then you will have to write a corresponding JsonSerializer.
Another option, not quite as robust, would be too simply use a map to store the likes and dislikes exactly as is. This solution would omit any explicit mappings for likes/dislikes and utilize the #JsonAny annotation to capture them. In this scheme the Person object would look like this:
public class Person {
private String lastName;
private String firstName;
private String email;
private String town;
#JsonAny
private Map<String, Object> otherProperties;
// Constructors, getters/setters
}
Deserializing your JSON into this modified version of Person will place all unrecognized properties into the hash map, as key-value pairs.
I'm pretty sure you can't do it the way you intend, how about doing it like this:
{
"NOM": "Doe",
"PRENOM": "John",
"EMAIL": "john.doe#email.me",
"VILLE": "Somewhere",
"likings": ["Lolcats", "Loldogs", "LIKE3": "Lolwut", "Bad stuff", "Bad bad stuff" ]
}

Categories

Resources