How to dynamically handle json response array/object using Gson - java

I am facing a problem , sometimes Json response returns an array of objects , sometimes object itself , how we can handle dynamically in the response class.
In the current eg :results sometimes gets an array of objects
"\"results\": " +
"[{" +
and sometimes object itself
"\"results\": " +
"{" +
Eg:
How we can handle this ?
Gson gson = new Gson();
SearchResponse response=new SearchResponse();
response= gson.fromJson("{" +
"\"completed_in\": 0.047," +
"\"max_id\": 291771567376039936," +
"\"max_id_str\": \"291771567376039936\"," +
"\"next_page\": \"?page=2&max_id=291771567376039936&q=javacodegeeks\"," +
"\"page\": 1," +
"\"query\": \"javacodegeeks\"," +
"\"refresh_url\": \"?since_id=291771567376039936&q=javacodegeeks\"," +
"\"results\": " +
"{" +
"\"created_at\": \"Thu, 17 Jan 2013 04:58:57 +0000\"," +
"\"from_user\": \"hkokko\"," +
"\"from_user_id\": 24726686," +
"\"from_user_id_str\": \"24726686\"," +
" \"from_user_name\": \"Hannu Kokko\"," +
" \"geo\": null," +
"\"id\": 291771567376039936," +
"\"id_str\": \"291771567376039936\"," +
"\"iso_language_code\": \"en\"," +
" \"metadata\": {" +
"\"result_type\": \"recent\"}," +
"\"profile_image_url\": \"hjh\"," +
"\"profile_image_url_https\": \"kkj\"," +
"\"source\": \"<a href="hj;\"," +
"\"text\": \"Continuous Deployment: Are You Afraid It Might Work? jh\"," +
"\"to_user\": null," +
"\"to_user_id\": 0," +
"\"to_user_id_str\": \"0\"," +
"\"to_user_name\": null" +
" }," +
"\"results_per_page\": 15," +
"\"since_id\": 0," +
"\"since_id_str\": \"0\"" +
"}", SearchResponse.class);
System.out.println(response.toString());
Kindly assist...
Can anyone give any suggestions by using different jars to achieve this?

i found a solution for this ,i felt to share this..The code will automatically convert ..if excepted response is arraylist in response class....then if object is coming in response then add to arraylist else if arraylist it will take the same list.
we need hook change the response bfore it calls fromJson.
public class ArrayAdapter<T> extends TypeAdapter<List<T>> {
private Class<T> adapterclass;
public ArrayAdapter(Class<T> adapterclass) {
this.adapterclass = adapterclass;
}
public List<T> read(JsonReader reader) throws IOException {
List<T> list = new ArrayList<T>();
Gson gson = new Gson();
if (reader.peek() == JsonToken.BEGIN_OBJECT) {
T inning = (T) gson.fromJson(reader, adapterclass);
list.add(inning);
} else if (reader.peek() == JsonToken.BEGIN_ARRAY) {
reader.beginArray();
while (reader.hasNext()) {
T inning = (T) gson.fromJson(reader, adapterclass);
list.add(inning);
}
reader.endArray();
} else {
reader.skipValue();
}
return list;
}
public void write(JsonWriter writer, List<T> value) throws IOException {
}
}
public class ArrayAdapterFactory implements TypeAdapterFactory {
#SuppressWarnings({ "unchecked" })
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
ArrayAdapter typeAdapter = null;
try {
if (type.getRawType() == List.class)
{
typeAdapter = new ArrayAdapter(
(Class) ((ParameterizedType) type.getType())
.getActualTypeArguments()[0]);
}
} catch (Exception e) {
e.printStackTrace();
}
return typeAdapter;
}
then just call
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ArrayAdapterFactory()).create();
SearchResponse response;
esponse= gson.fromJson("your json string", SearchResponse.class)

You would need to write a custom deserializer that checks the type of results in the JSON then acts accordingly.
Your POJO will contain an array for results and if your incoming JSON only has a single object you'll need to fix that. One way is to modify the JSON then deserialize it:
class SearchResponseDeserializer implements JsonDeserializer<SearchResponse> {
public SearchResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
if (json.getAsJsonObject().get("results").isJsonObject()) {
//modify JSON: change results to be an array
// ...
}
return new Gson().fromJson(json, SearchResults.class);
}
}
Or, fix the server, of course. It should always be returning an array to avoid this issue.

Related

How to put Json object through loop

I want to loop through the questionnaires by using the for loop to set the value of the EMAIL_TO_CLIENT_IND.
The value inside the "path" is QUESTIONNAIRES/SUB_QUESTION/EMAIL_TO_CLIENT_IND.
The first "if" is if the level path in JSON is just one Ex. "Questionnaires". And the "else" is for the path more than 1 Ex. "Questionnaires/Question".
My current idea is put the jObject.getJSONObject() inside the "else" for loop.
You can use the JSONArray as follows
String json="{\"QUESTIONNAIRES\":[{\n" +
" \"SUB_QUESTION\":[{\n" +
" \"EMAIL_TO_CLIENT_IND\":\"N\"\n" +
" }]\n" +
"}]}";
try {
JSONObject mainObject=new JSONObject(json);
JSONArray questionnairesArray=mainObject.getJSONArray("QUESTIONNAIRES");
for (int i=0;i<questionnairesArray.length();i++){
JSONObject questionnairesObject= (JSONObject) questionnairesArray.get(i);
JSONArray subQuestionArray=questionnairesObject.getJSONArray("SUB_QUESTION");
for (int j=0;j<subQuestionArray.length();j++){
JSONObject subQuestionObject= (JSONObject) subQuestionArray.get(i);
subQuestionObject.put("EMAIL_TO_CLIENT_IND","Y");
}
}
Log.e("Output",mainObject.toString());
} catch (JSONException e) {
Log.e("Exception",e.getMessage());
}
Edited
can be solved using GSON library also
//frame data classes
class Response {
#SerializedName("QUESTIONNAIRES")
private List<Questionaire> questionaireList;
public List<Questionaire> getQuestionaireList() {
return questionaireList;
}
public void setQuestionaireList(List<Questionaire> questionaireList) {
this.questionaireList = questionaireList;
}
}
class Questionaire {
#SerializedName("SUB_QUESTION")
private List<SubQuestion> subQuestionList;
public List<SubQuestion> getSubQuestionList() {
return subQuestionList;
}
public void setSubQuestionList(List<SubQuestion> subQuestionList) {
this.subQuestionList = subQuestionList;
}
}
class SubQuestion {
#SerializedName("EMAIL_TO_CLIENT_IND")
private String emailToClientId;
public String getEmailToClientId() {
return emailToClientId;
}
public void setEmailToClientId(String emailToClientId) {
this.emailToClientId = emailToClientId;
}
}
//loop through data using GSON
Gson gson = new Gson();
String json = "{\"QUESTIONNAIRES\":[{\n" +
" \"SUB_QUESTION\":[{\n" +
" \"EMAIL_TO_CLIENT_IND\":\"N\"\n" +
" }]\n" +
"}]}";
Response response = gson.fromJson(json, Response.class);
for (Questionaire questionnaire : response.getQuestionaireList()) {
for (SubQuestion subQuestion : questionnaire.getSubQuestionList()) {
subQuestion.setEmailToClientId("Y");
}
}
Log.e("Output", gson.toJson(response));

How do i pass a class as in the params in junits?

I have an API :
#RequestMapping(value = "/area/create",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<AreaEntry> createNewAreaEntry(
#RequestBody AreaRequestDto areaRequestDto) throws ServiceException {
UserContextHolder.setCustomer(areaRequestDto.getAreaEntry().getHost());
UserContextHolder.setSsoId(areaRequestDto.getUser());
AreaEntry newAreaEntryDto = service.createNewAreaEntry(areaRequestDto.getAreaEntry());
System.out.println("The area entries" + areaRequestDto.getUser()
+ " " + " "
+ areaRequestDto.getAreaEntry().getName()
+ " "
+ areaRequestDto.getAreaEntry().getCode() + " deugging");
System.out.println("the new area entry dto is" + newAreaEntryDto);
return new ResponseEntity<AreaEntry>(newAreaEntryDto, HttpStatus.OK);
I need to write a JUnit:
private List<String> getInvalidAreas(String areas) throws Exception {
ResultActions actions = mockMvc.perform(get("/area/create").param("cities", areas));
}
Where I need to pass the class arearequest dto in params
How do i do it?
You can simply pass json string, it will automatically deserialize and convert to DTO.
Or you can create utility method that convert your dto object to json and pass it as request body.
public String asJsonString(final Object obj) throws JsonProcessingException {
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper.writeValueAsString(obj);
}

Can I ignore MismatchedInputException within ObjectMapper?

I'm using the Jackson ObjectMapper class like so:
objectMapper.treeToValue(jsonNode, MyClass.class)
where jsonNode is an instance of JsonNode.
When I call treeToValue(), I get a MismatchedInputException with the message
Cannot deserialize instance of com.example.MyField` out of START_OBJECT token
because MyField is defined as a String inside of MyClass, but it is a JSON object inside of the jsonNode variable. I am perfectly OK with jsonNode having a non-matching type for one of its fields, but I'd rather ObjectMapper just not try and serialize this field and ignore it instead of throwing the MismatchedInputException.
I've tried using
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
but this just ignores missing fields, it does not do anything to prevent the MismatchedInputException for an existing field.
In the post that diginoise referred, you should have a look at this response:
https://stackoverflow.com/a/40972234/9343066
You can recursively remove offending fields and try again. Here is what I came up with (can be simplified based on your logging and exception handling needs).
public class YourClass {
private static final Logger LOGGER = Logger
.getLogger(YourClass.class.getName());
public final static ObjectMapper mapper = new ObjectMapper().configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
public static <T> T toTypedObject(Class<T> type, JsonNode tree) {
return toTypedObject(type, tree, true);
}
private static <T> T toTypedObject(Class<T> type, JsonNode tree,
boolean topLevel) {
T object;
try {
object = mapper.treeToValue(tree, type);
} catch (MismatchedInputException e) {
String originalTree = tree.toString();
object = toTypedObject(type, tree, originalTree, e);
if (topLevel) {
LOGGER.log(Level.WARNING, "Failed to convert node tree to a "
+ type.getSimpleName()
+ " object without modifications: " + originalTree, e);
LOGGER.log(Level.INFO,
"Modified node tree was successfully converted to a "
+ type.getSimpleName() + " object: " + tree);
}
} catch (JsonProcessingException e) {
throw new YourException("Failed to convert node tree to a "
+ type.getSimpleName() + " object: " + tree, e);
}
return object;
}
private static <T> T toTypedObject(Class<T> type, JsonNode tree,
String originalTree,
MismatchedInputException mismatchedInputException) {
T object;
List<Reference> path = mismatchedInputException.getPath();
if (path != null && !path.isEmpty()) {
try {
ObjectNode subNode = (ObjectNode) tree;
for (int i = 0; i < path.size(); i++) {
String fieldName = path.get(i).getFieldName();
if (i + 1 < path.size()) {
subNode = (ObjectNode) tree.get(fieldName);
} else {
subNode.remove(fieldName);
}
}
object = toTypedObject(type, tree, false);
} catch (Exception e) {
throw new YourException("Failed to convert node tree to a "
+ type.getSimpleName() + " object: " + originalTree,
mismatchedInputException);
}
} else {
throw new YourException(
"Failed to convert node tree to a " + type.getSimpleName()
+ " object: " + originalTree,
mismatchedInputException);
}
return object;
}
}
Call with:
YourObject yourObject = YourClass.toTypedObject(YourObject.class, tree);

Json to Java Mapping

I want to map below Json data to java object of List<Map<String, String>> type.
Sample Json:
{
{
a:b,
c:d
},
{
e:f,
g:h,
i:j
},
{
h:k
}
}
Here a:b represents key-value pair. So a:b and c:d will be mapped to first map of the list and so on.
one way to do this is by building JSON tree and access each node and store the pair into the map.
Is there a better way to do this (cleaner approach)?
Here is the code to read a List<Map<String, String>> using the Jackson library, with your example data as input:
public class JsonTest {
public static void main(String[] args) throws Exception {
final String json
= "[\n"
+ " {\n"
+ " \"a\":\"b\",\n"
+ " \"c\":\"d\"\n"
+ " },\n"
+ " {\n"
+ " \"e\":\"f\",\n"
+ " \"g\":\"h\",\n"
+ " \"i\":\"j\"\n"
+ " },\n"
+ " {\n"
+ " \"h\":\"k\"\n"
+ " }\n"
+ "]"; // [{a:b,c:d},{e:f,g:h,i:j},{h:k}]
ObjectMapper mapper = new ObjectMapper();
TypeFactory factory = TypeFactory.defaultInstance();
List<Map<String, String>> list = mapper.readValue(json,factory
.constructCollectionType(List.class, factory
.constructMapType(Map.class, String.class, String.class)));
System.out.println(list.toString());
}
}
Note: I had to fix your outermost braces from {} to [], which is the correct JSON list syntax.
You can use Jackson to achieve this task through below example code
public class JacksonExample {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
try {
// Convert JSON string from file to Object
User user = mapper.readValue(new File("C:\\user.json"), User.class);
System.out.println(user);
// Convert JSON string to Object
String jsonInString = "{\"age\":33,\"messages\":[\"msg 1\",\"msg 2\"],\"name\":\"xyz\"}";
User user1 = mapper.readValue(jsonInString, User.class);
System.out.println(user1);
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
First I made few changes to your json to make it valid
{
"key":
[{
"a": "b",
"c": "d"
},
{
"e": "f",
"g": "h",
"i": "j"
},
{
"h": "k"
}]
}
Please find the below code that I have tried out :
ObjectMapper mapper = new ObjectMapper();
ObjectNode objectNode1 = mapper.createObjectNode();
Map<String, Object> i = mapper.readValue(new File("J:/temp/sample.json"), Map.class);
System.out.println(i.get("key"));
System.out.println(i.values());
Output :
//For System.out.println(i.get("key"));
[{a=b, c=d}, {e=f, g=h, i=j}, {h=k}]
//For System.out.println(i.values());
[[{a=b, c=d}, {e=f, g=h, i=j}, {h=k}]]
If the above approach helps you, Make a right decision based on your needs either i.get("key") or i.values()
Just use gson library to Json to object and object to Json
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
Object to Json
Gson gson = new Gson();
Student obj=new Student();
String jsonInString = gson.toJson(obj);
Json To Object
Student obj = gson.fromJson(jsonInString, Student.class);

how to solve.. abstract class? generics?

In my current setup I have a centralized helper class like the following:
#Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
if(task == null) {
return;
}
Gson gson = new GsonBuilder().create();
ResponseJson p = null;
try {
p = gson.fromJson(result, ResponseJson.class);
} catch(JsonSyntaxException e) {
//Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toast_sync_completed), Toast.LENGTH_SHORT).show();
Log.e("", e.getMessage() + " got '" + result + "'");
}
Log.i("", p.toString());
Log.i("", result);
if(p.status.equals(ResultStatus.SUCCESS.toString())) {
task.success(p.data);
} else if (p.status.equals(ResultStatus.FAIL.toString())) {
task.fail(p.data);
} else if (p.status.equals(ResultStatus.ERROR.toString())) {
task.error(p.message);
} else {
throw new UnsupportedOperationException();
}
}
the problem is i want to make the ResponseJson class to be dynamic and the data attribute to be unique (sometimes i need ArrayList<Something> other times HashMap<String, String>) works for converting my JSON result. can i achieve this using generics or some other means?
If you set Object as type of data,
class ResponseJson {
Object data;
}
Later you can get the results as ArrayList or Hashmap as follows;
if (responseJson.data instanceof ArrayList<?>) {
ArrayList<String> arrayList = (ArrayList<String>) responseJson.data;
} else if(responseJson.data instanceof HashMap<?, ?>) {
HashMap<String, String> hashmap = (HashMap<String, String>) responseJson.data;
}
Due to type erasure, the parameterised type cannot be known at runtime. But if you know the type you can do an unchecked cast.

Categories

Resources