Using Realm with Gson - java

I have json with field _id
String json = "{ _id : 1, name : 'Alex', role: 'admin' }"
In my Realm model I use #SerializedName attribute:
public class User extends RealmObject {
#SerializedName("_id")
#PrimaryKey
private int id;
private String name;
private String comment;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
If try save the json:
realm.createOrUpdateObjectFromJson(User.class, json)
field _id can't parsed and in database created record with id = 0
In docs using #SerializedName attribute
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class);
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}).create();
User user = gson.fromJson(json, User.class);
realm.beginTransaction();
realm.copyToRealmOrUpdate(user);
In this case json = "{ _id : 1, role: 'user' }" just remove user name from database, because default value for String is null.
So, probably I incorrectly using the attribute. How to consider the attribute when dealing with the methods of conservation of json (createOrUpdateObjectFromJson, etc)?

Why writing all these custom serializers when you can make Gson and
Realm work together with just ONE LINE OF CODE?
IMO, setting an exclusive strategy to every model we want to serialize is not the best approach. As you have realized, this approach requires writing a lot of boiler-plate code which is error prone and the worst of all, kills what Gson is about (which is making our lives less painful).
And since we're working-around incompatibilities, why don't you just make sure to pass an unmanged RealmObject to your Gson serializer?
Simplest solution ever (IMO)
Found here. Get a copy in memory of the managed RealmObject and pass it to Gson
new Gson().toJson(realm.copyFromRealm(managedModel));
And that's it! No more code to write!
Other good solutions and explanations are posted here.

You probably need a hybrid solution in order to make this work. The reason is that #SerializedName is a GSON annotation and not one that Realm knows about. So this means you have two choices as you already discovered:
1) Use GSON, which means the end result is an object with null as the default for name.
2) Use createOrUpdateObjectFromJson which means that _id will be ignored because Realm's JSON parser require a 1:1 mapping.
As neither solution will work correctly you can modify them in the following way:
1) With GSON, instead of doing copyToRealmOrUpdate you can manually search for the object and update the role:
realm.beginTransaction();
realm.where(User.class).equalTo("id", user.getId()).findFirst().setRole(user.getRole());
realm.commitTransaction();
2) Using just the pure JSON you can modify to match the expected format:
JSONObject obj = new JSONObject(json);
obj.put("id", obj.getString("_id"));
obj.remove("_id");
Realm has an issue tracking the request for custom mappings, but it has a low priority as that feature is usually better covered by frameworks such as GSON, Jacokson, etc. : https://github.com/realm/realm-java/issues/1470

Related

Handle Json non existent keys spring boot

I am creating a requestModel and let say a person doesn't send me some keys.
If that key is not present I want to put null if i get the value of the key.
I don't want to investigate if a key is present or not .
public class CustomerModel {
private Optional<String> s3Bucket;
private Optional<String> docType;
public String getS3Bucket() {
if(s3Bucket.isPresent()) {
return s3Bucket.get();
} else {
return null;
}
}
public void setS3Bucket(Optional<String> s3Bucket) {
this.s3Bucket = s3Bucket;
}
public Optional<String> getDocType() {
return docType;
}
public void setDocType(Optional<String> docType) {
this.docType = docType;
}
}
Do we have any library or something where.
1. If i get the key and it is not present in the coming request json, i will get the null out of it and if the key is present and has value . It will be stored as value.
2. When writing the getter for s3bucket (getS3Bucket), i dont want to write it for everykey value. Is there a automatic way to do this.
I looked at lot of posts but the scenario is not there.
P.S - I am new to java
I believe Jackson is exactly what you need. And if you are using Spring - it already uses Jackson under the hood I guess.
Here you can find some examples and documentation of how JSON mapping on to model class is done.
If you need to customize some behavior, you can use annotations like #JsonProperty (there are many).
If properties in your model class have the same names as properties in JSON, most probably you won't need to provide any further configs.
Here is a simple example:
public class User {
#JsonProperty("userName")
private String name;
private int age;
// getters and setters
}
And if you have JSON like this:
{
"userName" : "Foo Bar",
"age" : 18
}
Jackson will do all the magic for you unless you need something very specific.
If something is not in JSON you get (let's say you received JSON without age) - corresponding property in model class will be null if it is object type and default value (0, false, etc.) for primitives (in our case age would be 0).

Deserializing JSON object with dynamic JsonProperty without wrapper class using Jackson

I want to restructure an application so that it uses REST instead of an EJB3Factory which wasn't needed in the first place and only makes trouble with the new server.
Basically I have a class Request:
public class Request {
public String name;
public String id;
public List<? extends someObject> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The Request gets created and using Gson made into a Json object:
Gson gson = new Gson();
String payload = gson.toJson(Request);
This then gets sent to the REST API on the server. There Jackson deserializes it. I do not have access to the Jackson implementation there and cannot change it to Gson.
What I am basically trying to do now is to get Jackson to use the non-default constructor to deserialize the object. I know I can annotate the non-default constructor like this:
#JsonCreator
public Request(#JsonProperty("name") String name, #JsonProperty("id")
String id, #JsonProperty("list") List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The thing is though that the field name of list is set at runtime per reflection and the Gson object that is generated might have it as scenarioName1 for one and scenarioName2 for something else.
I have looked at the different solutions provided here on Stack Overflow but none of them could provide me with a solution for my problem. This seemed most helpful but I cannot under any circumstances use a wrapper property nor can I actually map all possibilities.
Anyone got any idea?
EDIT to include examples:
Example 1:
{"name":"someName","id":"first","someScenarioName":[{...}]}
Example 2:
{"name":"someOtherName","id":"second","differentScenarioName":[{...}]}
Since I'm out of town on business that is the best I can do with right now. It's basically the last field having a different name depending on which scenario was chosen beforehand.
Maybe you can try take a look on Mapper Features. Sincerely I didn't try it yet because I'm at work and so on, but I will send now my example and maybe it can help you:
public class Request {
public String name;
public String id;
public List<? extends T> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
}
Then to deserialize the object:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.USE_ANNOTATIONS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.readValue(json, Request.class);
My try is because the deserialization by annotation is true by default, but once you don't have a "list" object most of time, it won't find the field there.
Okay, so I figured out what my problem was. There are other lists in the class and those were the trouble. After annotating each of them with #JsonProperty("theirRespectiveName") it worked like a charm... Now I have to annotate about 100 lines of code and solve some more problems.

Serialization pojo into different json structure

I need to serialize a pojo into different json structure depending on whom I am sending request. Also I should be able to configure in some config that how field of pojo are mapped to json properties for a given request.
Can this be achived using jackson?
Is there some library or api to do this?
Edit:
For example:
public class Universal {
private int id;
private Date date;
private String name;
private Inner inner;
private Map<String,Object> others;
private List<Inner> inners;
}
public class Inner {
private String value;
}
now above are two object i need to create dynamic json, one example for some of transformation is below
{
"id":"",//value will be id of Universal
"detials":{
"name":"",//value will be name of Universal
},
"data":[], // array of value(field of Inner) from inners
"ext":{
"prop1":""// value of this field will be some (key1) value from others
}
}
You can use Google Gson and rely on its type adaptors.
http://www.javacreed.com/gson-typeadapter-example/ is a good article from web

Serialize Jackson field in two different ways

I am doing POJO serialization / deserialization using Jackson.
Here is a POJO exemple :
public class Pojo {
public String productId;
public String name;
}
I have to read the field productId in this JSON :
{"productId":"1","name":"exemple"}
But also in :
{"_id":"1","name":"exemple"}
To make it short, I would like to use the same object to read the field in a JSON file found somewhere and to save the object as this in MongoDB, using productId as the primary key, which has to be named _id.
Since I am using Jackson (fasterxml) both to read from the file and to write to the database, I can not find a way to do so, except by creating a new class with the same fields (or inheritance) and fill them one by one. Basically, I would like to find a way to put 2 #JsonProperty annotations on productId.
Works with both strings:
public class Pojo {
#JsonProperty("_id")
public String productId;
public String name;
#JsonProperty("productId")
public void setProductId(String id) {
productId = id;
}
}

How to deserialize the following json using Jackson

I have the following json:
{
"id":"myid",
"fields":{
"body":"text body"
}
}
which I want to deserialize into the following Java class:
class TestItem {
private String id;
private String body;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
using the Jackson Json deserializer. This doesn't work because the body field is nested inside a fields inner class.
I can't change the json structure, so is there any way (perhaps using annotations) I can remap of the body field up from TestItem.fields.body to TestItem.body?
Edit:
I should have said this is part of a larger class hierarchy and the aim of the excercise is to reduce the depth of it. In other words, I know that I COULD declare an inner class and then access that, but that is not what I'm trying to achieve.
There are couple of feature requests that (if implemented) would allow limited one-level wrapping/unwrapping. But currently there is no declarative way to do this. And to some degree it is edge case, since this goes into data transformation as opposed to data binding (unfortunately I can't think of good object transformation libs, so there may be bit of gap there).
What is usually done, then, is to do two-phase binding: first into intermediate types (often java.util.Map, or jackson JsonNode (Tree model)); modify those, and then convert from this type to actual result. For example something like this:
JsonNode root = mapper.readTree(jsonSource);
// modify it appropriately (add, remove, move nodes)
MyBean bean = mapper.convertValue(root, MyBean.class);

Categories

Resources