Gson property order in android - java

I have integrated Gson to create the json used in a request for an android application.
Here is my model class
public class TwitterUser {
#Expose
public String gid;
public String icon_url;
public Boolean is_app_user;
#Expose
public String displayName;
public TwitterUser(String l, String i, String url, Boolean app_user) {
gid = i;
displayName = l;
icon_url = url;
is_app_user = app_user;
}
public TwitterUser(String l, String i) {
gid = i;
displayName = l;
}
public String getGid() {
return gid;
}
public void setGid(String gid) {
this.gid = gid;
}
public String getIcon_url() {
return icon_url;
}
public void setIcon_url(String icon_url) {
this.icon_url = icon_url;
}
public Boolean getIs_app_user() {
return is_app_user;
}
public void setIs_app_user(Boolean is_app_user) {
this.is_app_user = is_app_user;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
Here is how i create the json request
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
gson.toJson(twitterUser));
But when I send the request to the server - the order will be rejected. I have to change the request's field order to stay:
gid
displayName
but gson creates other way around, is there any way to achieve this.

Gson doesn't support definition of property order out of the box, but there are other libraries that do. Jackson allows defining this with #JsonPropertyOrder, for example.
But of course Gson has it's way so you can do it by creating your very own Json serializer:
public class TwitterUserSerializer implements JsonSerializer<TwitterUser> {
#Override
public JsonElement serialize(TwitterUser twitterUser, Type type, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.add("gid", context.serialize(twitterUser.getGid());
object.add("displayName", context.serialize(twitterUser.getDisplayName());
// ...
return object;
}
}
Then of course you need to pass this serializer to Gson during Setup like this:
Gson gson = new GsonBuilder().registerTypeAdapter(TwitterUser.class, new TwitterUserSerializer()).excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(twitterUser);
See also:
Gson User Guide - Custom serializers and deserializers

Related

Gson parse class with generic item

I have base class WebAnswer,
public class WebAnswer<T> {
private int id;
private T result;
private ErrorModel error;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public ErrorModel getError() {
return error;
}
public void setError(ErrorModel error) {
this.error = error;
}
}
and inherited class RequestAnsModel
public class RequestAnsModel extends WebAnswer<RequestModel> {
}
I need to deserialize JSON object with Gson, in code I do it like this:
Gson gson = new Gson();
WebAnswer<RequestModel> data = new WebAnswer<RequestModel>();
data = gson.fromJson(response.toString(),data.getClass());
and in Field result I get array of objects com.google.gson.internal.LinkedTreeMap$Node#. But when I do something like this
WebAnswer<RequestModel> data = new RequestAnsModel();
I get correct model where result is object of RequestModel. Are there any ways to desirialize in gson somthing like this correct with Generic without create inherited class?
You should use TypeToken like in the code below:
Gson gson = new Gson();
Type type = new TypeToken<WebAnswer<RequestModel>>() {
}.getType()
WebAnswer<RequestModel> data = gson.fromJson(response.toString(), type);

Mapping JSON into POJO using Gson

I have the following JSON to represent the server response for a salt request:
{
"USER":
{
"E_MAIL":"email",
"SALT":"salt"
},
"CODE":"010"
}
And i tried to map it with the following POJO:
public class SaltPOJO {
private String code = null;
private User user = null;
#Override
public String toString() {
return this.user.toString();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public class User {
private String e_mail = null;
private String salt = null;
#Override
public String toString() {
return this.e_mail + ": " + this.salt;
}
public String getE_mail() {
return e_mail;
}
public void setE_mail(String e_mail) {
this.e_mail = e_mail;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
}
Now everytime i do this:
Gson gson = new Gson();
SaltPOJO saltPojo = gson.fromJson(json.toString(), SaltPOJO.class);
Log.v("Bla", saltPojo.toString());
The saltPojo.toString() is null. How can i map my JSON into POJO using Gson?
Is the order of my variables important for the Gson mapping?
Is the order of my variables important for the Gson mapping?
No, that's not the case.
How can i map my JSON into POJO using Gson?
It's Case Sensitive and the keys in JSON string should be same as variable names used in POJO class.
You can use #SerializedName annotation to use any variable name as your like.
Sample code:
class SaltPOJO {
#SerializedName("CODE")
private String code = null;
#SerializedName("USER")
private User user = null;
...
class User {
#SerializedName("E_MAIL")
private String e_mail = null;
#SerializedName("SALT")
private String salt = null;
You don't have proper mapping between your getter and setter. If you change your json to something like below, it would work:
{
"user":
{
"email":"email",
"salt":"salt"
},
"code":"010"
}
If you are getting json form third party then unfortunately, you would have to change your pojo or you could use adapter.

How to right declare a composite java JSON object from string?

i have this string which i'm using for testing of api:
{"limit":30, "offset":"0", "filters": [{"property":"vlc.vlc","operator":"=","value":"DEKU113829"}]}
I would like to create JSOn object for processing in Android app using the:
JSONObject json = new JSONObject();
json.put("limit", 30);
json.put("offset", "0");
But i don't know how to create "filters" section using put method...
What is the right and most effective solution for this?
JSONObject/JSONArray supports a "builder-esque" pattern and put can be chained - it will return the same (but modified) object.
JSONObject json =
new JSONObject()
.put("limit", 30)
.put("offset", "0") /* but should be 0? */
.put("filters",
new JSONArray()
.put(new JSONObject()
.put("property", "vlc.vlc")
.put("operator", "=")
.put("value", "DEKU113829")
)
);
Alternatively, look into a POJO mapper like Gson, which I would recommend overall for ease of use and consistency.
Try this...
JSONObject json = new JSONObject();
json.put("limit", 30);
json.put("offset", "0");
JSONArray js_array = new JSONArray();
JSONObject json_obj = new JSONObject();
json_obj.put("property", "vlc.vlc");
json_obj.put("operator", "=");
json_obj.put("value", "DEKU113829");
js_array.put(json_obj);
json.put("filters",js_array);
Use the Gson Library. Your model objects for the JSON will be
public class MainModel
{
private int limit ;
public int getlimit()
{
return this.limit;
}
public void setlimit(int limit)
{
this.limit = limit;
}
private String offset ;
public String getoffset()
{
return this.offset;
}
public void setoffset(String offset)
{
this.offset = offset;
}
private ArrayList<Filter> filters ;
public ArrayList<Filter> getfilters()
{
return this.filters;
}
public void setfilters(ArrayList<Filter> filters)
{
this.filters = filters;
}
}
public class Filter
{
private String property ;
public String getproperty()
{
return this.property;
}
public void setproperty(String property)
{
this.property = property;
}
private String operator ;
public String getoperator()
{
return this.operator;
}
public void setoperator(String operator)
{
this.operator = operator;
}
private String value ;
public String getvalue()
{
return this.value;
}
public void setvalue(String value)
{
this.value = value;
}
}
You can populate your filter object and create MainModel object and add the filter object to it. Next use Gson library as below to get your json string
Gson gsonParser = new Gson();
String jsonString = gsonParser.toJson(mainModelObject);

Json to Object using Gson

I have a class DocumentBO which has the following attributes -
public class DocumentBO implements IStorageBO {
private String aId;
private String studyId;
private Map<AlgorithmsEnum, JobIOStatus> status;
private String text;
private Collection<Sentence> sentences;
public String getaId() {
return aId;
}
public void setaId(String aId) {
this.aId = aId;
}
public String getStudyId() {
return studyId;
}
public void setStudyId(String studyId) {
this.studyId = studyId;
}
public Map<AlgorithmsEnum, JobIOStatus> getStatus() {
return status;
}
public void setStatus(Map<AlgorithmsEnum, JobIOStatus> status) {
this.status = status;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Collection<Sentence> getSentences() {
return sentences;
}
public void setSentences(Collection<Sentence> sentences) {
this.sentences = sentences;
}
}
The AlgorithmsEnum is as follows -
public enum AlgorithmsEnum {
SENTIMENT("sentiment"),
INTENTION("intention"),
TOPIC("topic"),
NER("ner"),
UIMA("uima");
private final String value;
private AlgorithmsEnum(String value) {
this.value = value;
}
public String value() {
return value;
}
#Override
public String toString() {
return value;
}
public static AlgorithmsEnum fromValue(String value) {
if (value != null) {
for (AlgorithmsEnum aEnum : AlgorithmsEnum.values()) {
if (aEnum.value().equals(value)) {
return aEnum;
}
}
}
return null;
}
}
The JobIOStatus is also similar.
I am successfully able to create a JSON string of Collection using GSON using the following TypeToken
Type type = new TypeToken<Collection<DocumentBO>>() {}.getType();
But, when I try to recreate the Collection object using the JSON string returned by Gson and the same TypeToken, the key of the status hashmap is always returned as NULL whereas the value is successfully created. What do you think can be the issue?
The problem is that you have overridden toString() in your enum.
If you look at the JSON being produced, the keys to your Map<AlgorithmsEnum, JobIOStatus> are the lowercase names you're creating. That won't work. Gson has no idea how to recreate the enum from those when you attempt to deserialize the JSON.
If you remove your toString() method it will work just fine.
Alternatively you can use the .enableComplexMapKeySerialization() method in GsonBuilder when serializing which will ignore your toString() method and produce JSON using the default representations of your enum values which is what is required.
There are "well" known :) issues of Gson to serialize Map when the key is derived from object and its not a "native" data type.
Please use this
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.enableComplexMapKeySerialization().create();
Collection<DocumentBO> obj = gson.fromJson(str, type);

How to expose a method using GSon?

Using Play Framework, I serialize my models via GSON. I specify which fields are exposed and which aren't.
This works great but I'd also like to #expose method too. Of course, this is too simple.
How can I do it ?
Thanks for your help !
public class Account extends Model {
#Expose
public String username;
#Expose
public String email;
public String password;
#Expose // Of course, this don't work
public String getEncodedPassword() {
// ...
}
}
The best solution I came with this problem was to make a dedicated serializer :
public class AccountSerializer implements JsonSerializer<Account> {
#Override
public JsonElement serialize(Account account, Type type, JsonSerializationContext context) {
JsonObject root = new JsonObject();
root.addProperty("id", account.id);
root.addProperty("email", account.email);
root.addProperty("encodedPassword", account.getEncodedPassword());
return root;
}
}
And to use it like this in my view:
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Account.class, new AccountSerializer());
Gson parser = gson.create();
renderJSON(parser.toJson(json));
But having #Expose working for a method would be great: it would avoid making a serializer just for showing methods!
Check out Gson on Fire: https://github.com/julman99/gson-fire
It's a library I made that extends Gson to handle cases like exposing method, results Post-serialization, Post-deserialization and many other things that I've needed over time with Gson.
This library is used in production in our company Contactive (http://goo.gl/yueXZ3), on both Android and the Java Backend
Gson's #Expose seem to only be supported on fields. There is an issue registered on this: #Expose should be used with methods.
Couple different options based on Cyril's answer:
Custom serializer with a shortcut:
public static class Sample
{
String firstName = "John";
String lastName = "Doe";
public String getFullName()
{
return firstName + " " + lastName;
}
}
public static class SampleSerializer implements JsonSerializer<Sample>
{
public JsonElement serialize(Sample src, Type typeOfSrc, JsonSerializationContext context)
{
JsonObject tree = (JsonObject)new Gson().toJsonTree(src);
tree.addProperty("fullName", src.getFullName());
return tree;
}
}
public static void main(String[] args) throws Exception
{
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Sample.class, new SampleSerializer());
Gson parser = gson.create();
System.out.println(parser.toJson(new Sample()));
}
-OR-
Annotation based serializer
public static class Sample
{
String firstName = "John";
String lastName = "Doe";
#ExposeMethod
public String getFullName()
{
return firstName + " " + lastName;
}
}
public static class MethodSerializer implements JsonSerializer<Object>
{
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context)
{
Gson gson = new Gson();
JsonObject tree = (JsonObject)gson.toJsonTree(src);
try
{
PropertyDescriptor[] properties = Introspector.getBeanInfo(src.getClass()).getPropertyDescriptors();
for (PropertyDescriptor property : properties)
{
if (property.getReadMethod().getAnnotation(ExposeMethod.class) != null)
{
Object result = property.getReadMethod().invoke(src, (Object[])null);
tree.add(property.getName(), gson.toJsonTree(result));
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return tree;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD) //can use in method only.
public static #interface ExposeMethod {}
public static void main(String[] args) throws Exception
{
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Sample.class, new MethodSerializer());
Gson parser = gson.create();
System.out.println(parser.toJson(new Sample()));
}

Categories