Deserializing json that is false or object with Retrofit GsonConverterFactory - java

I am working with a server that returns json. One of the elements is either an object or false - it it is non exiting. I know this is very poor implementation of server response and there are quite a few such cases, but this is what I have to work with. How can I deal with this situation? If there is an object I successfully deserialze it, if none - i get error - EXPECTED OBJECT FOUND BOOLEAN.
Even worse, I do not know where I am gonna meet such situations in future on this project.
This is the sample json:
{
"course": {
"id": "47902",
"course": "3844",
"group": "1825",
"teacher": "59502",
"table": "1447",
"client": "1",
"course_id": "3844",
"description": ""
},
"teacher": {
"id": "59502",
"post": "0",
"experience": "",
"dep_experience": "",
"rank": "0",
"online": "1458891283",
"departments": [
null
]
},
"depart": {
"id": "100",
"postcode": "",
"public": "1",
"alias": "",
"faculty": "97",
"client": "1"
},
"progress": false,
"files": [
{
"teacher": {
"id": "59502",
"code": "53bd7c21ad05b03e",
"photo": "1"
},
"files": [
{
"id": "0ffe41e5003ee5c0",
"owner": "59502",
"address": "0ffe41e5003ee5c0",
"type": "f",
"size": "0",
"time": "2015-07-10 14:39:15",
"data": ""
}
]
}
]
}
As you can see progress is false here. Other times it is ordinary object like depart. Deserialization is done by Retrofit 2.
Thanks a lot.

I'm assuming you have a top-level mapping similar to the following one and have configured your Retrofit instance for Gson:
final class Response {
#SerializedName("progress")
#JsonAdapter(FalseAsNullTypeAdapterFactory.class)
final Progress progress = null;
}
final class Progress {
final String foo = null;
}
Note that the progress property is annotated with the #JsonAdapter annotation: we're assuming this is only place were the progress property can be a boolean (if you have many places like this one, you can either annotate each field with this annotation, or .registerTypeAdapter() via GsonBuilder; in case of .registerTypeAdapterFactory() the factory must check against known types in order not to "intercept" all types).
Now, here is a type adapter factory to deal with your issue:
final class FalseAsNullTypeAdapterFactory
implements TypeAdapterFactory {
// Let Gson instantiate it itself
private FalseAsNullTypeAdapterFactory() {
}
#Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Get a downstream parser (for simplicity: get the default parser for the given type)
final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
return new TypeAdapter<T>() {
#Override
public void write(final JsonWriter out, final T value) {
throw new UnsupportedOperationException();
}
#Override
public T read(final JsonReader in)
throws IOException {
// Peek whether the next JSON token is a boolean
if ( in.peek() == BOOLEAN ) {
// And take the this JSON token as a boolean value
// Is it true?
if ( in.nextBoolean() ) {
// Then it's not something we can handle -- probably a boolean field annotated with #JsonAdapter(FalseAsNullTypeAdapterFactory.class)?
throw new MalformedJsonException("Unexpected boolean marker: true");
}
// We're assuming it's null
return null;
}
// If it's not a boolean value, then we just delegate parsing to the original type adapter
return delegateTypeAdapter.read(in);
}
};
}
}
Now just test it:
try ( final Reader reader = getPackageResourceReader(Q43231983.class, "success.json") ) {
final Response response = gson.fromJson(reader, Response.class);
System.out.println(response.progress.foo);
}
try ( final Reader reader = getPackageResourceReader(Q43231983.class, "failure.json") ) {
final Response response = gson.fromJson(reader, Response.class);
System.out.println(response.progress);
}
where the given resources are:
success.json is {"progress":{"foo": "bar"}};
failure.json is {"progress":false}.
The output is as follows:
bar
null

Related

How I can get List object from JSON instead of a LinkedHashMap?

I have this method which should return different objects from JSON, depending on the type of class in the argument.I tryed it to return a list of objects based on the argument, but I get only LinkedHashMap into ArrayList.
I searched a lot, but everywhere in the solutions the class type is hard-coded.
Is there a way to solve this problem without hard code?
public static <T> List<T> getObjects(Class<T> c) {
CloseableHttpClient rest = HttpClientSessionSingleton.getInstance().getHttpClient();
String urlRequest = (host + "/" +
c.getSimpleName().toLowerCase() + "s");
HttpGet httpGet = new HttpGet(urlRequest);
try (CloseableHttpResponse response = rest.execute(httpGet)) {
HttpEntity entity = response.getEntity();
String jsonString = EntityUtils.toString(entity);
List<T> listObjectFromJson = new ObjectMapper().readValue(jsonString, new TypeReference<List<T>>(){});
return listObjectFromJson;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
I want to just pass the class type and get objects through one method.
[
{
"id": "73cbc0b5-3dd5-49c4-97cb-6225a19122b5",
"name": "Management",
"fields": [
{
"id": "c2d740d5-4d47-42ae-b616-977b40327812",
"name": "newField1"
}
]
},
{
"id": "dd74384b-717d-4368-b0e4-3f441d5b1ffc",
"name": "IT",
"fields": []
},
{
"id": "03304335-d7d7-46ca-8075-8d5e9feb43c6",
"name": "hhh",
"fields": []
},
{
"id": "e11b4c3f-080e-490d-8ef4-ea301d551a5d",
"name": "NEWWWWW",
"fields": []
},
{
"id": "fec7eeb0-0845-49be-be14-6cdb5fcd3575",
"name": "NEWWWWW",
"fields": []
},
{
"id": "50dfea14-f30a-448c-99df-10bf01d088fa",
"name": "NEWWWWW",
"fields": []
},
{
"id": "a4a1224e-7c66-484c-ae87-dc2ecc058c36",
"name": "NEWWWWW",
"fields": []
}
]
I get this exception when my object has a relationship
Unrecognized field "fields" (class model.orm.Department), not marked as ignorable (2 known properties: "id", "name"])
at [Source: (String)"[{"id":"73cbc0b5-3dd5-49c4-97cb-6225a19122b5","name":"Management","fields":[{"id":"c2d740d5-4d47-42ae-b616-977b40327812","name":"newField1"}]},{"id":"dd74384b-717d-4368-b0e4-3f441d5b1ffc","name":"IT","fields":[]},{"id":"03304335-d7d7-46ca-8075-8d5e9feb43c6","name":"hhh","fields":[]},{"id":"e11b4c3f-080e-490d-8ef4-ea301d551a5d","name":"NEWWWWW","fields":[]},{"id":"fec7eeb0-0845-49be-be14-6cdb5fcd3575","name":"NEWWWWW","fields":[]},{"id":"50dfea14-f30a-448c-99df-10bf01d088fa","name":"NEWWWWW","fie"[truncated 84 chars]; line: 1, column: 77] (through reference chain: java.util.ArrayList[0]->model.orm.Department["fields"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from
You can construct a new JavaType parametric type passing as an argument the List.class to the ObjectMapper.html#getTypeFactory method like below:
public static <T> List<T> getObjects(Class<T> c) throws IOException {
//omitted the lines before creating the mapper including the jsonstring
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametricType(List.class, c);
return mapper.readValue(jsonString, type);
}

Returning a GeoJSON object in java

I am new to GeoJSON and im currently running into an issue that I just don't know how to do it.
Here is a snippet of what my FeatureCollection looks like
FeatureCollection fc = new FeatureCollection();
Map<String, Object> properties = new HashMap<String, Object>();
for(int i = 0; i < platforms.size(); i ++) {
Feature feature = new Feature();
Point geometry = new Point();
Dto dto = platforms.get(i);
Position position = new Position(dto.getLat(), dto.getLon());
fc.addFeature(feature);
geometry.setCoordinates(position);
feature.setGeometry(geometry);
feature.setProperties(properties);
properties.put("name", dto.getName());
properties.put("msl", dto.getMsl());
properties.put("id", dto.getId());
}
return fc.toString();
I want my output to look like this:
{
"type":"FeatureCollection"
features":[
{
"type":"Feature"
"geometry": {
"type":"Point"
"coordinates":[
-120.200000,
10.100000
]
},
"properties": {
"name": "1"
"height": "100.00"
"id": "null"
}
}
{
"type":"Feature"
"geometry": {
"type":"Point"
"coordinates\":[
-130.200000,
20.100000
] \n "
}, \n "
"properties": { "
"name": "2"
"height": "100.00"
"id": "null"
}
}
]
}
As far as I can tell, through debugging, the correct information is being placed into the feature but whenever I return the featureCollection I get this:
mil.nga.sf.geojson.FeatureCollection#366ac49b
I don't know much about geojson but it seems like I'm incorrectly returning the FeatureCollection and its printing out its name or whatever.
Simply put, how do I print the contents of a FeatureCollection?
EDIT:
This is the output that I get after implementing gson.
{
"features": [
{
"feature": {
"geometry": {
"x": 10.1,
"y": -120.2,
"z": null,
"m": null,
"geometryType": "POINT",
"hasZ": false,
"hasM": false
},
"properties": {
"msl": 100.0,
"name": "1",
"id": null
}
},
"id": null,
"bbox": null,
"foreignMembers": {}
},
{
"feature": {
"geometry": {
"x": 20.1,
"y": -130.2,
"z": null,
"m": null,
"geometryType": "POINT",
"hasZ": false,
"hasM": false
},
"properties": {
"msl": 100.0,
"name": "2",
"id": null
}
},
"id": null,
"bbox": null,
"foreignMembers": {}
}
],
"bbox": null,
"foreignMembers": {}
I am unsure what to do moving forward to get this to mirror my desired output.
Your problem is that you're calling fc.toString();, which will hit the default Object.toString() method. This will dump some classname+address/id-like String, depending on th JVM used.
Instead of calling toString(), you should use a JSON library like google gson, and add
a few lines to the bottom of your code:
final GsonBuilder gb = new GsonBuilder();
gb.setPrettyPrinting();
gb.serializeNulls();
// gb.excludeFieldsWithoutExposeAnnotation(); // maybe use for more control
final Gson gson = gb.create();
final String jsonText = gson.toJson(fc);
return jsonText;
Also consider writing a utility class that does this for you with default settings that you choose:
public class MyJsonUtil {
static public String toJSON(final Object pObject) {
final GsonBuilder gb = new GsonBuilder();
gb.setPrettyPrinting();
gb.serializeNulls();
// gb.excludeFieldsWithoutExposeAnnotation(); // maybe use for more control
final Gson gson = gb.create();
final String jsonText = gson.toJson(pObject);
return jsonText;
}
}
and then at the end of your code you simply call return MyJsonUtil.toJSON(fc).

I want to map json object nested inside another object to POJO using jackson library

I have an object nested inside another object in Json file. I want to map this object with fields to a Model class.
{
"code": 200,
"time": "2019-09-05T07:09:44.228+0000",
"data": {
"statuses": [
{
"statusType": "IN_PROGRESS",
"statusTimestamp":"019-09-05T17:04:54+1000"
},
{
"statusType": "SENT",
"statusTimestamp":"2019-09-05T21:04:55+1000"
},
{
"statusType": "OPENED",
"statusTimestamp":"2019-09-05T23:04:55+1000"
},
{
"statusType": "INTERACTION_ID_RECEIVED",
"statusTimestamp":"2019-09-06T00:04:55+1000"
}
]
},
"status": 200,
"message": null,
"errors": null,
}
I want to map the statusType and TimeStamp to a custom model class.
Model Class:
public class Model{
private String statusType;
private DateTime statusTimestamp;
public Model(String statusType, String statusTimestamp) {
this.statusType=statusType;
this.statusTimestamp=new DateTime(statusTimestamp);
}
public String getStatusType() {
return statusType;
}
public void setStatusType(String statusType) {
this.statusType = statusType;
}
public DateTime getStatusTimestamp() {
return statusTimestamp;
}
public void setStatusTimestamp(String statusTimestamp) {
this.statusTimestamp = new DateTime(statusTimestamp);
}
}
I want to map the statuses to this model class and store these objects in a link something like this
List statuses = ParsedJson.read("$..['statuses'][*]", List.class)
If you don't want to model the entire response, you could use Jackson to parse the JSON into tree nodes and then map only the parts you care about:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json);
JsonNode statusesNode = rootNode.path("data").path("statuses");
List<Status> statuses = mapper.convertValue(statusesNode,
new TypeReference<List<Status>>(){});
I saw 2 errors in the json string fixing it should help you to create Object using java or any other language easily.
line 12: " was missing
line 26: invalid placement of ,
Json after fixing issues
{
"code": 200,
"time": "2019-09-05T07:09:44.228+0000",
"data": {
"statuses": [
{
"statusType": "IN_PROGRESS",
"statusTimestamp":"019-09-05T17:04:54+1000"
},
{
"statusType": "SENT",
"statusTimestamp":"2019-09-05T21:04:55+1000"
},
{
"statusType": "OPENED",
"statusTimestamp":"2019-09-05T23:04:55+1000"
},
{
"statusType": "INTERACTION_ID_RECEIVED",
"statusTimestamp":"2019-09-06T00:04:55+1000"
}
]
},
"status": 200,
"message": null,
"errors": null
}

Generic solution to GSON json object Array with a key

I am trying to find a generic solution in GSON for my project. This JSON has been problematic for me...
I have a class System
public class System{
String systemid;
String systemname;
//getter and setter
}
Rest service sends data in one of two below format, now for the second format I am handling it in a generic way as shown in last, can someone please help me to handle both the formats in a generic way in one piece of code, I am stuck on this from past two days now...
[
{
"atypes": [
{
"systemid": "123",
"systemname": "abc"
},
{
"systemid": "456",
"systemname": "def"
},
{
"systemid": "789",
"systemname": "ghi"
},
{
"id": "0123",
"name": "klm"
},
{
"systemid": "4567",
"systemname": "nop"
}
]
}
]
Or the second format
[
{
"systemid": "123",
"systemname": "abc"
},
{
"systemid": "456",
"systemname": "def"
},
{
"systemid": "789",
"systemname": "ghi"
},
{
"id": "0123",
"name": "klm"
},
{
"systemid": "4567",
"systemname": "nop"
}
]
Now I am handling the last JSON array in the below method, I want to handle both the code in one piece of generic code.
String data = client.executeCommand(Command.GET, new GenericUrl(URL), null);
System[] tList1 = JsonUtil.jsonArrayToObjectArray(data, System[].class);
which calls a generic piece of code
public static <T> T[] jsonArrayToObjectArray(String data, Class<T[]> tClass) throws Exception {
return new Gson().fromJson(data, tClass);
}
Please if someone can help me...
Edit:
This is different from identifying Json object and json array as here both are json array.

How to get specific object from RestTemplate exchange method?

I have an end-point which returns me this response:
{
"head": {
"status": 200,
"ok": true,
"messages": [],
"errors": [],
"references": {}
},
"body": {
"id": "d57a9c7aef9842c2e31a0f49c",
"flowId": "f57979d06f9842c3e94f1f197",
"creationDate": 1470744494732,
"path": "/luafanti/test",
"version": 0,
"elems": {
"xxx": {
"type": "integer",
"value": 200
}
}
}
}
My question is, how to make a model that can be populated with only a part of my json response. For example, with this:
"xxx": {
"type": "integer",
"value": 200
}
or this:
"elems": {
"xxx": {
"type": "integer",
"value": 200
}
}
Using Jackson, you can define your model as the following:
#JsonIgnoreProperties(ignoreUnknown=true)
public class MyResponseModel {
private Body body;
public void setBody(Body body) {this.body = body;}
public Body getBody() {return body;}
#JsonIgnoreProperties(ignoreUnknown=true)
public static class Body {
private Elems elems;
// getter and setter for elems
}
#JsonIgnoreProperties(ignoreUnknown=true)
public static class Elems {
private Xxx xxx;
// getter and setter for xxx
}
#JsonIgnoreProperties(ignoreUnknown=true)
public static class Xxx {
private String type;
private String value;
// getter and setter for type and value
}
}
The above is quite verbose, particularly if you are only interested in a very small part of the response. It may be more practical to handle the response as a String and then use e.g. JsonPath to extract only the data you are interested in.
You can use simple-json.jar to extract that object from inside the JSONObject
Downloadable Jar Link - simple-json.jar Download Link
Maven Jar Import Maven Repository pom syntax
You actual object is
{
"head": {
"status": 200,
"ok": true,
"messages": [],
"errors": [],
"references": {}
},
"body": {
"id": "d57a9c7aef9842c2e31a0f49c",
"flowId": "f57979d06f9842c3e94f1f197",
"creationDate": 1470744494732,
"path": "/luafanti/test",
"version": 0,
"elems": {
"xxx": {
"type": "integer",
"value": 200
}
}
}
} // hold this complete object in any string reference Variable.
Here I Suppose String jsonString holds the complete json object as above described.
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.JSONObject;
// implemetation logic for extracting jsonObject.
private JSONObject extractFirstBlock(String jsonString) throws ......{
JSONObject jsonObj ;
JSONParser parser=new JSONParser(); // parser to parse string to JSONObject
jsonObj = (JSONObject) parser.parse(jsonString); // parse the Object using parse Method.
String desiredObject = (String) jsonObj.get("body"); // this Object contains your desired output which you wish to receive.
jsonObj = parser.parse(desiredObject);
desiredObject = jsonObj.get("elems"); // you will get your desired object as you expected.
}
Here in desiredObject you will get your expected Values as JSONObject.
"xxx": {
"type": "integer",
"value": 200
}

Categories

Resources