I am writing and android add and using retrofit2 to send data to the server.
I am trying to exclude one field from the the object that is being serialized and sent to the server.
The object is named "Cheer" and I try to exclude the field id from it when being sent to the server. iv'e tried using #Expose(false, false) and explained here and tried to make the field private, but it is still sent to the server. See the api, object and call below. Please note, it workes, the object is added to the server, the only issue is that, id is still sent in the JSON and I need to exclude it from it.
Thanks!!!
public class Cheer {
#Expose(deserialize = false, serialize = false)
private int id;
}
public interface CheersAPI {
String BASE_URL = "url:port";
#POST("/cheers")
Call<Cheer> AddCheer(#Body Cheer cheer);
}
cheersAPI.AddCheer(cheerToAdd).enqueue(new Callback<Cheer>(){
#Override
public void onResponse(Call<Cheer> call, Response<Cheer> response) {
Log.d("in the on response", "done creating a cheer");
}
#Override
public void onFailure(Call<Cheer> call, Throwable t) {
Log.d("failed", "failed to add a cheer here!");
}
});
I assume you're using Gson. You can use transient.
private transient int id;
If you require a more complicated solution, take a look at Gson: How to exclude specific fields from Serialization without annotations
Kotlin
just use the annotation #Transient to exclude from the request any variable in your POJO
Example
data class Group(val group_id: String,
val group_name: String,
#Transient val isChecked:Boolean = false)
Transient documentation
/**
* Marks the JVM backing field of the annotated property as `transient`, meaning that it is not
* part of the default serialized form of the object.
*/
Visit here for details.
Basically,
#Expose will not be regarded by the default Gson instance. In order to utilize it, you'll need to use a custom Gson instance:
GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
Gson gson = builder.create();
but if you do this you'll have to add #Expose to every field in all your model classes or they won't be serialised or deserialised by GSON.
This is how to do it with Retrofit
val gson: Gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
builder = Retrofit.Builder()
.client(okHttpClient)
.baseUrl(URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
And in your model
// These values are read as JSON objects only in server response
#SerializedName("someField")
#Expose(serialize = false, deserialize = true)
var someField: String? = null
For example here, we will not send JSON object to server (deserialize = false) but we will receive it as response (deserialize = true)
Related
I have an API that returns some arguably useful metadata along with requested data itself. It looks something like this:
{
"success": true,
"messages": [],
/* other metadata */
"result": { /* fields with useful data */ }
}
So, basically I want to serialize only stuff that is nested inside of "result" field, preferably still being able to work with meta (checking "success" on true/false and reading messages might be useful).
I thought I could use JSONObject to separate "result" and other meta, but this pipeline feels like a bit of overhead. Is there a way to do it purely with GSON?
The other problem is that I use Retrofit, which has a very neat workflow with pure GSON. If the above is the only adequate way of dealing with such API, how should I approach integrating it into Retrofit workflow?
to your retrofit builder add:
.addConverterFactory(new GsonConverterFactory(new GsonBuilder()
.registerTypeAdapter(Result.class, new JsonDeserializer<Result>() {
#Override
public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if(!(((JsonObject) json).getAsJsonPrimitive("success")).getAsBoolean()) {
return null;
}
JsonObject result = ((JsonObject) json).getAsJsonObject("result");
return new Gson().fromJson(result, Result.class);
}
}).create()))
there are npe and other checks to do of course :)
Create a POJO with #Expose annotation and use serialization = true/false. If you want to serialize only success, then your POJO would look something like this.
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class POJO {
#SerializedName("success")
#Expose(serialize = true, deserialize = false)
private Boolean success;
///Your getter / setter methods
}
I have used this above with Retrofit and it works fine.
Hope this helps!
EDIT:
Also you need to mention this while creating your Retrofit Service
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.excludeFieldsWithoutExposeAnnotation();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(YOUR_BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
.build();
I am trying to parse this JSON which is coming as the response to a REST API call. Can you please help me parsing it as key value pairs?
The object names are not present. There is nesting as well. There seems to be no new line between records.
The aim is to extract this data and load it into a database.
[
{
"cc_emails":["feedback#xyz.com"],
"fwd_emails":[],
"reply_cc_emails":["feedback#xyz.com"],
"fr_escalated":false,
"spam":false,
"email_config_id":6000038087,
"group_id":6000110481,
"priority":1,
"requester_id":6010410791,
"responder_id":6002817857,
"source":1,
"company_id":null,
"status":2,
"subject":"fare",
"to_emails":["feedback#xyz.com"],
"product_id":null,
"id":45043,
"type":null,
"due_by":"2016-03-12T08:58:02Z",
"fr_due_by":"2016-03-08T08:58:02Z",
"is_escalated":false,
"description":"Dear xyze Team,\r\n\r\nWhy r u increased fair again and againasas0mail.gmail.com</a>.<br>\n",
"custom_fields":
{
"category":null,
"issue":null,
"route_id":null,
"phone_number":null,
"department":null,
"booking_id":null
},
"created_at":"2016-03-07T08:58:02Z",
"updated_at":"2016-03-07T08:58:03Z",
// ...... repeat
}
]
The best way to do this would be to use http://www.jsonschema2pojo.org/
Enter your json there
Change source type to JSON
set the correct class name and package.
The resulting pojo can be directly mapped from the json
If you are using resttemplate to hit the api then you can use getForObject to automatically set the pojo from the output.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject-java.lang.String-java.lang.Class-java.lang.Object...-
Using gson you can do this quite simply.
Do a class to match the fields in the json something like:
public class Example {
private List<String> cc_emails;
private List<String> fwd_emails;
private List<String> reply_cc_emails;
private Boolean fr_escalated;
private Boolean spam;
private Integer email_config_id;
...
private CustomFields custom_fields;
private String created_at;
private String updated_at;
}
Then you need to do another to map the custom fields
public class CustomFields {
private String category;
...
}
And using json you can parse it like this:
Type type = new TypeToken<Collection<Example>>(){}.getType();
new Gson().fromJson(json,type);
You have to exaplain to Gson it's a list, if it was a single object it would be this:
new Gson().fromJson(json,Example.class);
This is the aproach I usually take, also in the dates java.sql.Timestamp class might also parse it, you would need to try it though.
You can use Gson (https://github.com/google/gson) or Jackson (https://github.com/FasterXML/jackson) and deserialize it to a Map.
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());
I receive from server a response in this form
{"error":null,"id":1,"result":
{"admin":false,
"firstname":"Jason",
"id":346,"idHotel":109,
"idVendor":null,
"lastname":"Butcher",
"sessionkey":"3c8a17ae47a6d131b1a14b44a1d8f9a9",
"urlAvatar":"avatar_316_mjm.jpg",
"urlThumb":"thumb_316_mjm.jpg"}
}
And want to get the various singles attributes,
for example
Boolean error=..;
String admin=....;
String idHotel=...;
and also the images
You should create a wrapper class like this:
public class Response {
public boolean error;
public int id;
public Result result;
}
public class Result {
...
}
then is simple for you to deserialize the json via gson to you classes:
Response response = new Response();
Gson gson = new Gson();
response = gson.fromJson(response, Response.class);
And there's a lot of other nifty things you can do: https://sites.google.com/site/gson/gson-user-guide
Oh forgot one important thing!
Remember to change the namespace on the Gson library, I had problems get it running on htc telephones. You can do this with jar jar links: http://code.google.com/p/jarjar/downloads/list
docs: http://code.google.com/p/jarjar/wiki/CommandLineDocs
I am using Play Framework 1.2.4 with Java and using JPA to persist my database objects. I have several Model classes to be rendered as JSON. But the problem is I would like to customize these JSON responses and simplify the objects just before rendering as JSON.
For instance, assume that I have an object named ComplexClass and having properties id, name, property1,...,propertyN. In JSON response I would like to render only id and name fields.
What is the most elegant way of doing this? Writing custom binder objects or is there simple JSON mapping such as using a template?
Play Framework 1.2.4 directly depends on the gson library so you could use that to render your JSON strings. All you have to do is use gson's #Expose annotation. So in your example, you would mark the fields you want in your JSON string like this:
public class ComplexClass {
#Expose
public Long id;
#Expose
public String name;
...
}
Then in your controller, you would just do this:
public static void someActionMethod() {
// get an instance of your ComplexClass here
ComplexClass complex = ...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()
String json = gson.toJson(complex);
renderJson(json);
}
See documentation here.
If ComplexClass is actually a play.db.jpa.Model and therefore the id field is abstracted away in a parent class and you can't put the #Expose annotation on it, then you could create your own ExclusionStrategy that skips fields that aren't annotated with #Expose and are not called id. So something like this (pseudo-code):
public final class ComplexClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes attributes) {
if (name of field is "id") return false;
if (field is annotated with #Expose) return false;
return true;
}
Then the controller would altered slightly to look like this:
GsonBuilder builder = new GsonBuilder();
ComplexClassExclusionStrategy strategy = new ComplexClassExclusionStrategy();
builder.setExclusionStrategies(strategy);
Gson gson = builder.create();
String json = gson.toJson(complex);
renderJson(json);
Use FlexJSON, it's really easy. It allows you to create JSONSerializers which can include/exclude the fields you want.
Check out this article for some examples of using it with Play! Framework.
Here's a simple example:
public ComplexClass {
public Long id;
public String name;
// And lots of other fields you don't want
public String toJsonString() {
// Include id & name, exclude all others.
JSONSerializer ser = new JSONSerializer().include(
"id",
"name",
).exclude("*");
return ser.serialize(this);
}
}
You can add it to your dependencies.yml like so:
require:
- play
- net.sf.flexjson -> flexjson 2.1
What I usually do is write an interface for models that implements a toJSONString() method so that I can call renderJSON(someModel.toJSONString()) in the controller.
Link to official website
EDIT: Extra example for lists/collections
Ok, when you start serializing list you might get some unexpected results. This is because the order of evaluation is important. The first include() or exclude() takes precedence over the following ones.
Here's an example of serializing the childs of a parent entity (OneToMany relation).
JSONSerializer ser = new JSONSerializer();
// Exclude these standard fields from childs
ser.exclude(
"*.persistent",
"*.class",
"*.entityId"
);
// Include childs and all its other fields
ser.include(
"childs",
"childs.*"
);
// Exclude everything else
ser.exclude("*");
String data = ser.serialize(parent);
The * is a wildcard by the way. This piece of documentation explains it perfectly:
An exclude of *.class will match to any path depth. So if flexjson is serializing the field with path of "foo.bar.class" the * in *.class will match foo.bar.