Convert complex Java object to json with gson.toJsonTree - java

I need to convert a java object (called org) to json format.
The object (DTO ) is a bit complex, because it contains a list of objects of the same class and which in turn can also contain more objects of the same class ( built recursively). When I passing the object to gson.toJsonTree method it seems to fail (there isnt any error), but it seems that the method does not like complex objects). If I set to null the list of objects of the first object everything works fine. I can not modify the class, only the method that makes json.
JsonElement jsonUO = null;
jsonUO = gson.toJsonTree(org,OrgDTO.class);
jsonObject.add("ORG", jsonUO)
public class OrgDTO implements Serializable{
private String id;
......
private List sucesores;
public OrgDTO(){
this.sucesores = new ArrayList();
}
.....
}

It might be a little bit late for the questioner, however I share my answer in case someone else face similar issue:
You'll need to create a helper class that does the json serialization. It should implement the JsonDeserializer:
public class OrgDTOJsonSerializer implements JsonDeserializer<OrgDTO> {
#Override
public JsonElement serialize(OrgDTO src, Type type, JsonSerializationContext jsc) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", src.getId());
/// Build the array of sucesores (whatever it means!)
JsonArray sucesoresArray = new JsonArray();
for (final OrgDTO obj: src.getSucesores()) {
JsonObject succJsonObj = serialize(obj, type, jsc);
sucesoresArray.add(succJsonObj);
}
jsonObject.add("sucesores", sucesoresArray);
return jsonObject;
}
}
Then you'll need to register it in gson before attempting to serialize any object of that type:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(OrgDTO.class, new OrgDTOJsonSerializer());

Related

Field of custom object not serializing through gson

In my app I use gson to save a list of custom objects List<AppBasicView> appViews. The objects are instances of AppBasicView (AppBasicView objects and child objects of that class).
The AppBasicView class is built like that:
public class AppBasicView {
enum BasicViewType {ImageView}
private BasicViewType mBasicViewType;
private LinearLayout.LayoutParams mLayoutParams;
public AppBasicView(BasicViewType basicViewType, LinearLayout.LayoutParams layoutParams) {
this.mBasicViewType = basicViewType;
this.mLayoutParams = layoutParams;
}
...
(getters&setters)
}
AppTextView - the child class of AppBasicView, is built like that:
public class AppTextView extends AppBasicView {
enum TextViewType {Button, TextView, EditText}
private TextViewType mTextViewType;
private String mText;
public AppTextView(TextViewType textViewType, LinearLayout.LayoutParams layoutParams, String mText) {
super(null, layoutParams);
this.mTextViewType = textViewType;
this.mText = mText;
}
...
(getters&setters)
}
I'm saving the list like that:
Gson gson = new Gson();
String json = gson.toJson(appViews);
spEditor.putString("app1objects", json);
spEditor.apply();
Problem 1
The json string I get when saving AppBasicView object contains only the mBasicViewType field (and doesn't contain mLayoutParams field).
And when I save AppBasicView child, the json string contains only child's additional fields and doesn't contain any of the parents (AppBasicView) fields (neither mBasicViewType nor mLayoutParams).
Can't understand why I'm not getting those fields serialized.
Problem 2
After deserialization I get the objects list with only AppBasicView views (even if they where AppTextView) that are not recognized as child objects of AppBasicView (for an AppBasicView v that was AppTextView, v instanceof AppTextView returns false).
This is the deserialization code:
String json = appDataPreferences.getString("app1objects", "");
Type listType = new TypeToken<ArrayList<AppBasicView>>(){}.getType();
if(!json.equals("null"))
appViews = new Gson().fromJson(json, listType);
How can I get the full children object and use them as they were and not as up-casted objects?
Thanks for any help in advance!
This is how I add objects to the list (in case it could help to find the answer):
AppBasicView appBasicView;
if(...)
{
appBasicView = new AppTextView(...);
}
else if(...)
{
appBasicView = new AppBasicView(...);
}
else
throw new CustomExceptions.InvalidDroppingViewTag();
...
appViews.add(appBasicView);
...
Solution 1
Disappeared fields turned out to be all null so gson didn't mention them because:
While serializing, a null field is omitted from the output.
To still see those fields you need to create your Gson like that: Gson gson = new GsonBuilder().serializeNulls().create();

Gson deserialize current object with extended class

I'm new to java (Hum... No... I learned Java at school 10 years ago but never really used it since today).
I have an object class which corresponds to my json and was generated with the website http://www.jsonschema2pojo.org/ (simplified here) :
public class ServerDatasObject {
private Integer error;
private Boolean isOffline;
public Integer getError() {
return error;
}
public Boolean getIsOffline() {
return isOffline;
}
}
And another class used to access all object data (simplified too) :
public class ServerDatasHandler extends ServerDatasObject {
public ServerDatasHandler(String json) {
Gson gson = new Gson();
// how to populate current object using : gson.fromJson(json, ServerDatasObject.class);
}
}
The question is in the code: how to populate current object?
I searched and found something about InstanceCreator :
final Foo existing;
InstanceCreator<Foo> creator = new InstanceCreator<Foo>() {
public Foo createInstance(Type type) { return existing; }
}
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, creator).create();
Foo value = gson.fromJson(jsonString, Foo.class);
// value should be same as existing
but I don't understand how to use it and if it is what I need.
The final goal is to
{
ServerDatasHandler serverDatasHandler = new ServerDatasHandler (json);
do something with serverDatasHandler.getError()
}
Thanks for your help
You can create a separate static method which creates your handler from json:
public static ServerDatasHandler fromJsonConfig(String json) {
Gson gson = new Gson();
ServerDatasHandler handler = gson.fromJson(json, ServerDatasHandler.class);
return handler;
}
Of course you can also move this method to a factory.
I never used InstanceCreator inside constructor because parsing JSON is almost never a task for a constructor. Preferably it should be hidden inside framework or your own factories. Though, your example with InstanceCreator should also work if you will return ServerDatasHandler.this from the createInstance method.

Parse first element of JSON list with GSON

In my project I'm using GSON to serialize and deserialize objects. Often I get a list of objects as JSON from a server but I'm only interested in the first element of the list. Is it possible with #SerializedName to only fetch the first element of the list?
I think about something like this: #SerializedName("List[0]")
Or what would you recommend to only parse the first element and not the whole list?
You should use a custom JsonDeserializer:
private class MyCustomDeserializer implements JsonDeserializer<MyModel> {
#Override
public MyCustomDeserializer deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {
// initialize an instance of your model
MyModel myModel = new MyModel();
JsonArray jArray = (JsonArray) json; // get json array
JsonObject jsonObject = (JsonObject) jArray.get(0); // get first object
// do what you want with the first object
myModel.setParameter(jsonObject.get("parameter").getAsInt());
// ignore next json objects
return myModel;
}
}
Then, initialize your Gson instance like this:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyModel.class, new MyCustomDeserializer());
Gson gson = gsonBuilder.create();
MyModel model = gson.fromJson(jsonString, MyModel.class);
If you want to exclude some fields from Serialization you need to declare them in your model as transient:
private transient String name; // will be ignored from Gson

An RPC mechanism which handles type hierarchy correctly in java

I need a mechanism for calling remote methods on server which handles type hierarchy correctly. For example GWT can do this, but my client isn't javascript! By handling type hierarchy, I mean transfering child objects even when the type is declared as a parent class! Suppose we have a class Container:
class Parent {
String name;
}
class Child extends Parent {
String family;
}
class Container {
Parent p;
}
And I have a method on server with the following signature:
void doSomethingImportant(Container c) {}
If I call this method on client, and pass an instance of Container, which has an instance of Child as property "p", I expect to get an instance of Child on server too (which would have "family" property)!
GWT handles this without any problem, is there any other technologies that can handle this?
I didn't find a RPC mechanism for this, but I managed to use JSON in order to handle this. I found Gson which is google's API for using JSON in java. It converts objects to JsonElements which can be interpretted as Strings and vice versa.
So the key feature that helped me develop this, was Gson's custom Serializer/Deserializer. I implemented a class which is Serializer and Deserializer for Object, and I send the class name for source class along the class's content:
class MySerializerAndDeserializer implements JsonSerializer<Object>, JsonDeserializer<Object>{
public JsonElement serialize(Object src, Type typeOfSrc,
JsonSerializationContext context) {
Class clazz = src.getClass();
JsonElement serialize = context.serialize(src);
JsonArray array = new JsonArray();
array.add(new JsonPrimitive(clazz.getName()));
array.add(serialize);
return array;
}
public Object deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonArray array = json.getAsJsonArray();
String asString = array.get(0).getAsString();
Object deserialize = null;
try {
deserialize = context.deserialize(array.get(1).getAsJsonObject(), Class.forName(asString));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return deserialize;
}
}
and then I registered MySerializerAndDeserializer for Parent.class:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Parent.class, new MySerializerAndDeserializer());
And finally used gson and got the Instance I expected correctly:
String json = gson.toJson(container, Container.class);
Container containerFromJson = gson.fromJson(json, Container.class);
Using sfnrpc (http://code.google.com/p/sfnrpc) you can specify which type must be used optionally in the class arguments.
Hope this helps.
Object [] objArr = new Object[1];
TestObject to = new TestObject();
to.data = "HELLO,";
objArr[0] = to;
**Class [] classArr = new Class[1];
classArr[0] = TestObject.class;**
TestObject response = (TestObject)rpcc.invoke("#URN1","127.0.0.1:6878","echo",true,60,"", objArr,classArr);
sl.debug("The response was:"+response.data);

Is it possible to deserialize JSON property names with periods as a nested object using GSON?

This is an example of the kind JSON I'm trying to consume using GSON:
{
"person": {
"name": "Philip"
"father.name": "Yancy"
}
}
I was wondering if it were possible to deserialize this JSON into the following structure:
public class Person
{
private String name;
private Father father;
}
public class Father
{
private String name;
}
So that:
p.name == "Philip"
p.father.name == "Yancy"
Currently I am using #SerializedName to obtain property names containing a period, e.g.:
public class Person
{
private String name;
#SerializedName("father.name")
private String fathersName;
}
However, that's not ideal.
From looking at the documentation it doesn't appear to be immediately possible but there may be something I have missed - I'm new to using GSON.
Unfortunately I cannot change the JSON I'm consuming and I'm reluctant to switch to another JSON parsing library.
As far as I understand you can't do it in a direct way, because Gson will understand father.name as a single field.
You need to write your own Custom Deserializer. See Gson user's guide instructions here.
I've never tried it, but it doesn't seem to be too difficult. This post could be also helpful.
Taking a look at Gson's user guide and the code in that post, you'll need something like this:
private class PersonDeserializer implements JsonDeserializer<Person> {
#Override
public Person deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {
JsonObject jobject = (JsonObject) json;
Father father = new Father(jobject.get("father.name").getAsString());
return new Person(jobject.get("name").getAsString(), father);
}
}
Assuming that you have suitable constructors...
And then:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Person.class, new PersonDeserializer());
Gson gson = gsonBuilder.create();
Person person = gson.fromJson(jsonString, Person.class);
And Gson will call your deserializer in order to deserialize the JSON into a Person object.
Note: I didn't try this code, but it should be like this or something very similar.
I couldn't do this with just Gson. I need a new library 'JsonPath'. I used Jackson's ObjectMapper to convert the object to string but you can easily use Gson for this.
public static String getProperty(Object obj, String prop) {
try {
return JsonPath.read(new ObjectMapper().writeValueAsString(obj), prop).toString();
} catch (JsonProcessingException|PathNotFoundException ex) {
return "";
}
}
// 2 dependencies needed:
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
// https://mvnrepository.com/artifact/com.jayway.jsonpath/json-path
// usage:
String motherName = getProperty(new Person(), "family.mother.name");
// The Jackson can be easily replaced with Gson:
new Gson().toJson(obj)

Categories

Resources