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;
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.
My service can receive several different jsons, such as:
{
"event":"conversation_started",
"context":"context information",
"user":{
"id":"01234567890A=",
"name":"John McClane",
"avatar":"http://avatar.example.com",
"country":"UK",
"language":"en",
"api_version":1
},
"subscribed":false
}
or
{
"event":"message",
"message":{
"type":"text",
"text":"a message to the service",
"location":{
"lat":12.34,
"lon":12.34
}
}
}
or several else jsons. The only field that is the same for all jsons is "event". All other fields can be different (depends on "event" value).
So the question is: how to convert those jsons to java objects (without making messy code)? The only way I know is to manually check "event" value (like json.startsWith("{\n\"event\":\"message\"") but I'm sure that there is any simple decision for doing this.
There are three ways I've done this. The first is to do what you're suggesting - parse the JSON, check the type, and create the object. Be very careful with using a String parser as you may or may not have things like new lines. Instead, do something like:
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(eventString);
String eventType = jsonNode.get("event").asText();
if( eventType.equalsIgnoreCase("conversation_started")) {
// create ConversationStarted object using something like:
ConversationStarted conversationStarted = objectMapper.readValue( eventString, ConversationStarted.class );
}
This, of course, requires all classes to have a concrete POJO to allow for deserialization.
Another way is to do what many other programming languages do and have a key/value map. There are a few ways to do this. One is with the Jackson libraries:
Map<String, Object> map = objectMapper.readValue(eventString, new TypeReference<Map<String,Object>>(){});
Map<String, Object> user = (Map<String, Object>) map.get("user");
System.out.println( "conversation started - avatar is " + user.get("avatar"));
That way you can pass around the Map and extract as needed. Note that you still need to understand the structure of the JSON but you don't need to have a POJO for it.
Lastly is a variation on the second solution. Using JSONPath you can pull out what you need directly. Again you will want to first check out which type of event you have. Something like:
if( JsonPath.read(eventString, "$.event").equals("conversation_started") ) {
String avatar = JsonPath.read(eventString, "$.user.avatar");
System.out.println("conversation started - avatar is " + avatar);
}
The last two methods require you to pull out values one at a time as shown. The first solution gives you a full object to work with. It is your call as to what works best in your environment.
UPD: If you don't want to convert JSON String to JAVA Object via declaring a POJO, you can parse it to JSONObject(com.alibaba.fastjson.JSONObject)
public class Event {
public static void main(String[] args) {
String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";
JSONObject jsonObject = JSONObject.parseObject(jsonA);
String event = jsonObject.getString("event");
if (event.equals("message")) {
//do what you want to do
System.out.println("message event......");
} else if ("conversation_started".equals(event)) {
System.out.println("context information event......");
}
}
}
Declaring a class of Event as below, and then convert JSON String to a Event JAVA object.
#Data
public class Event {
private String event;
private String context;
private User user;
private boolean subscribed;
private Message message;
#Data
public static class User {
private String id;
private String name;
private String avatar;
private String country;
private String language;
private int api_version;
}
#Data
public static class Message {
private String type;
private String text;
private Location location;
#Data
public static class Location {
private double lat;
private double lon;
}
}
public static void main(String[] args) {
String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";
ObjectMapper objectMapper = new ObjectMapper();
try {
Event eventA = objectMapper.readValue(jsonA, new TypeReference<Event>() {
});
System.out.println(objectMapper.writeValueAsString(eventA));
Event eventB = objectMapper.readValue(jsonB, new TypeReference<Event>() {
});
System.out.println(objectMapper.writeValueAsString(eventB));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Use a JSON object. This is dynamic and can load any json. Then you can reference the event field consistently
Example 1
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"event\" : \"conversation_started\", \"context\" : \"context information\", \"user\" : { \"id\" : \"01234567890A=\", \"name\" : \"John McClane\", \"avatar\" : \"http://avatar.example.com\", \"country\" : \"UK\", \"language\" : \"en\", \"api_version\" : 1 }, \"subscribed\" : false }");
System.out.println(((String)root.get("event")));
Example 2
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"event\" : \"message\", \"message\" : { \"type\" : \"text\", \"text\" : \"a message to the service\", \"location\" : { \"lat\" : 12.34, \"lon\" : 12.34 } } }");
System.out.println(((String)root.get("event")));
Java object:
public class Foo {
#JsonProperty("name")
private String name;
#JsonProperty("surname")
private String surname;
// getters + setters
}
JSON:
{
"meta":{
"code":200
},
"data":[
{
"name":"John",
"surname":"Smith"
}
]
}
API call:
return restTemplate.getForEntity(requestUrl, Foo[].class).getBody();
Is it possible to parse "data" array without creating an additional wrapper class? I tried adding the #JsonRootName("data") annotation on top of my Java class, but it did not work.
You can try with:
import org.json.*;
JSONObject obj = new JSONObject(" .... ");
String name = obj.getJSONObject("data").getString("name");
I am trying to parse the response from the server something like this
public class TestClass {
public class TaskResponse {
private String id;
private List<String> links;
public String getId(){
return id;
}
}
public static void main(String args[]){
String response = "{
"task": {
"id": 10,
"links": [
{
"href": "http://localhost:9000/v1/115e4ad38aef463e8f99991baad1f809/os-hosts/svs144/onboard/10"
}
]
}
}";
JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(response).getAsJsonObject();
Gson gson = new Gson();
TaskResponse tskResponse = gson.fromJson(response, TaskResponse.class);
String taskId = tskResponse.getId();
System.out.println("The task Id is "+taskId);
}
}
In what I am originally doing I get the task id as null but in this code also which I have written above to try and understand the problem, Eclipse is giving error in the string response, it does not recognize it is a proper string.
Please see to it that I can't change the response string as it is coming from server. Any help of any kind or any links to sort out the error will be highly appreciated.
Thanx in Advance.
Gson will try to match the JSON-string to your class' structure.
Since your JSON starts with task : { ... } it will try to find a property task on the class TaskResponse. Since there's no such field, it won't set anything on your instance.
So either convert it more generally (e.g. using Map.class as the target) or add the task property to a wrapper class:
class TaskResponse {
private Task task;
}
class Task {
private Long id;
private List<Link> links;
}
class Link {
String href;
}
There's also a third option detailed in this answer.
inside the string " can not directly be use
u need to escape it by replacing it to \"
" ===> \"
String response = "{
\"task\": {
\"id\": 10,
\"links\": [
{
\"href\": \"http://`localhost`:9000/v1/115e4ad38aef463e8f99991baad1f809/os-hosts/svs144/onboard/10\"
}
]
}
}";