GSON mapping null value for POJO properties - java

I'm trying to use GSON 2.2.2 (for the very first time) to map JSON into a Java POJO. I'm hitting a 3rd party RESTful web service and this is an example of the JSON I'm getting back:
{
"response": {
"job":{
"eta":-1,
"status":"approved",
"mt":1,
"lc_tgt":"fr",
"body_src":"Please translated me.",
"body_tgt":"S'il vous plaît traduire moi.",
"unit_count":3,
"tier":"machine",
"credits":0,
"ctime":"2013-02-07 14:56:12.391963",
"lc_src":"en",
"slug":"0",
"job_id":"NULL"
}
},
"opstat":"ok"
}
The POJO I'm trying to map this into is:
public class Job {
// correlates to "eta"
private int eta;
// correlates to "body_src"
private String sourceBody;
// correlates to "ctime"
private java.util.Date creationTimestamp;
// Getters and setters for all 3 properties
}
When I run the following code, I don't get any exceptions, but the print statement just prints "null":
// Hit the 3rd party service and get the JSON (example above).
JSONObject json = hitRestfulWebService();
Gson gson = new Gson();
// json.toString = "{response":{"job":{ ..."
Job job = gson.fromJson(json.toString(), Job.class);
System.out.println(job.getSourceBody());
My only guess is that GSON can't figure out how to map the 3 JSON fields to my 3 Job properties. Can someone help me figure out what this mapping needs to be? Thanks in advance.

You can use annotations to define, which json field gets mapped to which object member, e.g.:
class SomeClass
{
#SerializedName("body-src")
String myString1;
#SerializedName("header-src")
String myString2;
...

public class Response{
private Job job;
//generate setter and getter
}
public class Job {
// correlates to "eta"
private int eta;
// correlates to "body_src"
private String sourceBody;
// correlates to "ctime"
private java.util.Date creationTimestamp;
// Getters and setters for all 3 properties
}
now in Gson
JSONObject json = hitRestfulWebService();
Gson gson = new Gson();
// json.toString = "{response":{"job":{ ..."
Job job = gson.fromJson(json.toString(), Response.class);

use not response but response.job
not
{ "response": {..
use
{ "eva": ..
this may help;
String a = "{\"response\": {\"job\":{\"eta\":-1,\"status\":\"approved\",\"mt\":1,\"lc_tgt\":\"fr\",\"body_src\":\"Please translated me.\",\"body_tgt\":\"S'il vous plaît traduire moi.\",\"unit_count\":3,\"tier\":\"machine\",\"credits\":0,\"ctime\":\"2013-02-07 14:56:12.391963\",\"lc_src\":\"en\",\"slug\":\"0\",\"job_id\":\"NULL\"}},\"opstat\":\"ok\"}";
Job j = I.gson().fromJson(
((JsonObject) ((JsonObject) new JsonParser().parse(a)).get("response")).get("job"), Job.class);
System.out.println(j.getEta());

Related

How would I use Jackson to flatten JSON with nested array?

I am using JackSon to parse the following JSON:
{
"AwardID": "1111111",
"AwardTitle": "Test Title",
"Effort":
"[
{
"PersonFirstName": "Jon",
"PersonLastName": "Snow"
}
]"
}
I would like to flatten this to be used in the following class:
public class Award {
private String awardId;
private String awardTitle;
private String personFirstName;
private String personLastName;
}
I have tried the following and have gotten the first two values, but I haven't been able to get the values from Effort trying to use JsonUnwrapped. I noted that it doesn't work with arrays, but I am trying the objectMapper.configure(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS, true) configuration in the main method used to get the values.
public class Award {
#JsonProperty("AwardID")
private String awardId;
#JsonProperty("AwardTitle")
private String awardTitle;
#JsonUnwrapped
private Effort effort;
}
public class Effort {
private String personFirstName;
private String personLastName;
}
Note that I only expect one value in the Effort array from the API response at this time.
What is recommended to try next? Thank you!
The easiest way is having a List<Effort> if you have a JSON Array.
If there is always 1 item for Effort, the returning JSON should not have Effort as a JSON Array and instead should be a JSON Object.
But if you can only handle it codewise, you can have something like this (Note that there should always contain one item in Effort, otherwise it will throw Exception):
public class Award {
#JsonProperty("AwardID")
private String awardId;
#JsonProperty("AwardTitle")
private String awardTitle;
#JsonProperty("Effort")
private Effort effort;
}
public class Effort {
#JsonProperty("PersonFirstName")
private String personFirstName;
#JsonProperty("PersonLastName")
private String personLastName;
}
And your ObjectMapper needs to be enabled with DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS as well:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS);
Award award = mapper.readValue(rawJson, Award.class); // rawJson is your JSON String
And it should have the following output:
Award(awardId=1111111, awardTitle=Test Title, effort=Effort(personFirstName=Jon, personLastName=Snow))
Note that the annotation #JsonUnwrapped can only apply on JSON Object, not JSON Array:
Value is serialized as JSON Object (can not unwrap JSON arrays using this mechanism)

Why this HashMap not formatted correctly in Jersey/Tomcat?

I'm testing Jersey, I wanted to mack a mock endpoint that produces this JSON object
{
"Flight1" : 757,
"Flight2" : 107,
"Flight3" : 637,
}
so I've written written this resource:
#GET
#Path("myjson")
#Produces(MediaType.APPLICATION_JSON)
public String getMyJson(#QueryParam ("test1") String lat, #QueryParam("test2") String lng) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("Flight 1", 765);
map.put("Flight 2", 657);
map.put("Flight 3", 908);
return map.toString();
}
But then I get this response when I call /myjson
{
Flight 1=765,
Flight 2=657,
Flight 3=908
}
Jersey already knows which element is a String and which element is an Integer, yet it format then as if they're all numbers.
Also the Json as it stands can not be formatted by the "pretty" formatter, and I believe this makes parsing it difficult by http clients.
So my question are:
Why is this happening?
How to avoid it and write simple mock JSON object for testing that is correctly formatted
You can add the Jaxb annotations to serialize and deserialize the response object directly without converting. For this you need to add the jersey's jaxb library so that when you jersey environment is getting booted, it can enable the auto conversion feature.
Example:
#Path("jaxbResource")
#Produces("application/xml")
#Consumes("application/xml")
public class UserResource {
#GET
public User[] getUserArray() {
List<User> userList = new ArrayList<User>();
userList.add(new User(1, "John"));
………
return userList.toArray(new User[userList.size()]);
}
}
#XmlRootElement
public class User {
private int id;
private String name;
public User() {}
public User(int id,String name) {
this.id = id;
this.name = name;
}
………
}
Hope this helps!!!
Why is this happening?
Because you are only making a toString of your HashMap.
Ex.
HashMap<String,String> stringStringHashMap = new HashMap<String, String>();
stringStringHashMap.put("a","b");
stringStringHashMap.put("b","b");
stringStringHashMap.put("c","b");
Will print {b=b, c=b, a=b}
How to avoid it and write simple mock JSON object for testing that is
correctly formatted
You can do this by using a lot of libraries(Gson, Jackson, JsonSimple,etc).
As this already answered what you want to make HashMap to Json
This has nothing to do with Jersey/Tomcat. For core Java programming, that is how best toString() method could process the map to String.
In order to do so you can convert to JSONObject using
String jon = JSONObject.valueToString(map);
System.out.println(jon);
OR even using the gson like
Gson gson = new Gson();
String json = gson.toJson(map);
System.out.println(json);

Parsing JSON from WebResource to a usable Java Object

i try to use some web api, so i do this
public static void main(String[] args) {
// Create Jersey client
Client client = Client.create();
// GET request to findBook resource with a query parameter
String getSoccersSeasonsUrl = "http://api.football-data.org/v1/soccerseasons";
WebResource webResourceGet = client.resource(getSoccersSeasonsUrl);
webResourceGet.header("X-Auth-Token", myToken);
ClientResponse response = webResourceGet.get(ClientResponse.class);
String output = response.getEntity(String.class);
System.out.println(output);
}
output
[{"_links":{"self":{"href":"http://api.football-data.org/v1/soccerseasons/394"},"teams":{"href":"http://api.football-data.org/v1/soccerseasons/394/teams"},"fixtures":{"href":"http://api.football-data.org/v1/soccerseasons/394/fixtures"},
"leagueTable":{"href":"http://api.football-data.org/v1/soccerseasons/394/leagueTable"}},
"id":394,
"caption":"1. Bundesliga 2015/16",
"league":"BL1",
"year":"2015",
"currentMatchday":24,
"numberOfMatchdays":34,
"numberOfTeams":18,
"numberOfGames":306,
"lastUpdated":"2016-03-01T20:50:44Z »}
how can i fill from this output directly in a java ArrayList of object like:
public class SoccerSeason {
public SoccerSeason() {
}
private long id;
private String caption;
private String league;
private String year;
private long currentMatchday;
private long numberOfMatchdays;
private long numberOfTeams;
private long numberOfGames;
private String lastUpdated;
}
when i try to get directly SoccerSeason output = response.getEntity(SoccerSeason.class); i have a classic com.sun.jersey.api.client.ClientHandlerException
what's missing in my code please? do you have any idea how to do this simply?
What you want is Google's GSON. It can be found with a quick google search, and it has a ton of easy to read documentation.
Add GSON to your projects dependencies/source code, add getters and setters for all of your class members to the class you've created and it should work beautifully.
It is used like this:
Gson gson = new Gson();
SoccerSeason newSoccerSeason = gson.fromJson(webApiResponse, SoccerSeason.class);
String lastUpdated = newSoccerSeason.getLastUpdated();
Where webApiResponse is a String representation of the JSON received as your web API's response. You can also define a class SoccerSeasonList which looks like this:
public class SoccerSeasonList {
ArrayList<SoccerSeason> seasonList;
// getters/setters
}
Of course, your incoming JSON would have to have an object called seasonList containing all of your SoccerSeason objects to match up with this definition.
But then, you could grab your list like so:
SoccerSeasonList seasonList = gson.fromJson(webApiResponse, SoccerSeasonList.class);
ArrayList<SoccerSeason> seasonArr = seasonList.getSeasonList();
And perform operations like so:
for(SoccerSeason ss : seasonArr)
System.out.println(ss.getNumberOfMatchdays());
To recap: You simply match up your JSON object names and literals to their equivalent java types in a class, and call fromJSON on a String containing the JSON received from your web API that you'd like to parse, passing in the class you want the object parsed to.

Multiple GSON #SerializedName per field?

Is there any way in Gson to map multiple JSON fields to a single Java object member variable?
Let's say I have a Java class...
public class MyClass {
String id;
String name;
}
I want to use this single class with two different services. However, these two services differ in how they return their data...
{ "id": 2341, "person": "Bob" }
... and ...
{ "id": 5382, "user": "Mary" }
... respectively.
Is there any way to map both the "person" and "user" fields in the JSON string to the name field in the Java object?
(Note: I only ever need to convert from JSON string to Java object - never the other way around.)
In October 2015, Gson version 2.4 (changelog) added the ability to use alternate/multiple names for #SerializedName when deserializing. No more custom TypeAdapter needed!
Usage:
java
#SerializedName(value="name", alternate={"person", "user"})
kotlin
#SerializedName(value="name", alternate= ["person", "user"])
https://www.javadoc.io/doc/com.google.code.gson/gson/2.6.2/com/google/gson/annotations/SerializedName.html
for Kotlin fans
#SerializedName(value="name", alternate= ["person", "user"])
It is not supported to define multiple #SerializedName annotations to a field at Gson.
Reason: By default Deserialization is managed with a LinkedHashMap and the keys are defined by incoming json's field names (not the custom class's field names or the serializedNames) and there is a one to one mapping. You can see the implementation(how deserialization works) at ReflectiveTypeAdapterFactory class's inner class Adapter<T>'s read(JsonReader in) method.
Solution:
You can write a custom TypeAdapter which handles name, person and user json tags and maps them to name field of your custom class MyClass:
class MyClassTypeAdapter extends TypeAdapter<MyClass> {
#Override
public MyClass read(final JsonReader in) throws IOException {
final MyClass myClassInstance = new MyClass();
in.beginObject();
while (in.hasNext()) {
String jsonTag = in.nextName();
if ("id".equals(jsonTag)) {
myClassInstance.id = in.nextInt();
} else if ("name".equals(jsonTag)
|| "person".equals(jsonTag)
|| "user".equals(jsonTag)) {
myClassInstance.name = in.nextString();
}
}
in.endObject();
return myClassInstance;
}
#Override
public void write(final JsonWriter out, final MyClass myClassInstance)
throws IOException {
out.beginObject();
out.name("id").value(myClassInstance.id);
out.name("name").value(myClassInstance.name);
out.endObject();
}
}
Test case:
String jsonVal0 = "{\"id\": 5382, \"user\": \"Mary\" }";
String jsonVal1 = "{\"id\": 2341, \"person\": \"Bob\"}";
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyClass.class, new MyClassTypeAdapter());
final Gson gson = gsonBuilder.create();
MyClass myClassInstance0 = gson.fromJson(jsonVal0, MyClass.class);
MyClass myClassInstance1 = gson.fromJson(jsonVal1, MyClass.class);
System.out.println("jsonVal0 :" + gson.toJson(myClassInstance0));
// output: jsonVal0 :{"id":5382,"name":"Mary"}
System.out.println("jsonVal1 :" + gson.toJson(myClassInstance1));
// output: jsonVal1 :{"id":2341,"name":"Bob"}
Examples about TypeAdapters.
Edit 2016.04.06 : As #Mathieu Castets has written at his answer, it is supported now. (That is the correct answer for this question.)
public abstract String[] alternate
Returns: the alternative names of
the field when it is deserialized Default: {}
For KOTLIN i used below but doesn't work
#SerializedName(value="name", alternate= ["person", "user"])
so i edited it and here it works fine!!
#SerializedName(value="name", alternate= arrayOf("person", "user"))

How to convert HTTP Request Body into JSON Object in Java

I am trying find a Java lib/api that will allow me to turn the contents of a HTTP Request POST body into a JSON object.
Ideally I would like to use a Apache Sling library (as they are exposed in my container naturally).
The closest I've found it: org.apache.sling.commons.json.http which converts the header to JSON.
HTTP Post bodies are in the format; key1=value1&key2=value2&..&keyn=valueN so I assume there is something out there, but I havent been able to find it.
I may just have to use a custom JSONTokener (org.apache.sling.commons.json.JSONTokener) to do this if something doesn't already exist. Thoughts?
Thanks
Assuming you're using an HttpServlet and a JSON library like json-simple you could do something like this:
public JSONObject requestParamsToJSON(ServletRequest req) {
JSONObject jsonObj = new JSONObject();
Map<String,String[]> params = req.getParameterMap();
for (Map.Entry<String,String[]> entry : params.entrySet()) {
String v[] = entry.getValue();
Object o = (v.length == 1) ? v[0] : v;
jsonObj.put(entry.getKey(), o);
}
return jsonObj;
}
With example usage:
public void doPost(HttpServletRequest req, HttpServletResponse res) {
JSONObject jsonObj = requestParamsToJSON(req);
// Now "jsonObj" is populated with the request parameters.
// e.g. {"key1":"value1", "key2":["value2a", "value2b"], ...}
}
Jackson is also a good option - its used extensively in Spring. Here is the tutorial: http://wiki.fasterxml.com/JacksonInFiveMinutes
I recommend trying Apache Commons Beanutils.
ServeltRequest request;
Map map = request.getParameterMap();
MyObject object = new MyObject();
BeanUtils.populate(object, map);
String json = object.toJSON() //using any JSON library
Sorry on making this an own answer but obviously my reputation doesn't allow me to simply add a comment to the answer How to convert HTTP Request Body into JSON Object in Java of maerics.
I would also iterate over the request params but instead of using an arbitrary json library use the JSONObject that is provided by sling. http://sling.apache.org/apidocs/sling6/org/apache/sling/commons/json/JSONObject.html
import org.json.JSONObject;
JSONObject json = new JSONObject(request.getParameterMap())
Use Gson. With this you can create class with private variables which represent the data you want : for example.
meta:{
name:"Example"
firstname:"Example2"
}
data:[
{
title:"ecaetra"
description:"qwerty"
}
...
]
Json Object could be retrieve like this :
public class RetrieveData {
private Meta meta;
private List<Data> data;
public Meta getMeta(){
return meta;
}
public List<Data> getData(){
return data;
}
}
public class Meta {
private String name;
private String firstname;
public String getName(){
return name;
}
public String getFirstName(){
return firstname;
}
}
public class Data {
private String title;
private String description;
public String getTitle(){
return title;
}
public String getDescription(){
return description;
}
}
And your instruction are simple. Content is the content of your Page, you can retrieve it with Asynctask.
Object o = new Gson().fromJson(Content, RetrieveData.class);
data = (RetrieveData)o;
// Get Meta
data.getName(); // Example
data.getFirstName(); // Example2
// Get Data
data.get(0).getTitle(); // position 0 : ecaetra
data.get(0).getDescription(); // position 0 : qwerty

Categories