I am writing a function which serialize a Java object into Json using Gson.
The problem I have is that it only serialize primitive fields of my class but not object fields. For example. I have two classes like:
class TestClass {
public int i = 10;
public TestClass2 tc2;
}
class TestClass2 {
public int j = 20;
}
And my test is:
#Test
public void shouldSerializeSimpleObjectIntoJson() {
TestClass tc = new TestClass();
String json = new Gson().toJson(tc);
System.out.println(json);
}
The Json output is:
{"i":10}
It does not contain the fields of tc2.
How can I config Gson to recursively encode an object into Json?
Thanks
The field is ignored because it's null. From the documentation:
The default behaviour that is implemented in Gson is that null object fields are ignored. This allows for a more compact output format; however, the client must define a default value for these fields as the JSON format is converted back into its Java.
Here's how you would configure a Gson instance to output null:
Gson gson = new GsonBuilder().serializeNulls().create();
If you set tc.tc2 to a value other than null, you should see its fields in the output file.
With your specific example, add a constructor which will make a new instance of tc2:
public TestClass()
{
tc2 = new TestClass2();
}
and see if it helps. If in your real code the class which is similar to TestClass2 has reference classes as well, create a new instance of them as well
In Java, object fields are not initialized by default, that means the tc2 field will be null unless you set it to something. Gson ignores null fields by default, and thus it's not serializing it.
You could initialize the field in the constructor, like the other answer correctly says. Or you could also initialize it doing the following:
class TestClass {
public int i = 10;
public TestClass2 tc2 = new TestClass2();
}
Related
I have a POJO with a field marked as transient. GSON does not serialize it. Great. But when it is deserialized it wipes out the the field's initial settings.
For example, if I have this object:
public class ObjWithTransient {
public String name;
public transient List<String> listOStrings = new ArrayList();
}
And I run this test:
#Test
public void testSerializeWithTransient() throws Exception {
ObjWithTransient obj = new ObjWithTransient();
obj.name = "Foobar";
String json = gson().toJson(obj);
// Deserialize
ObjWithTransient obj2 = GsonUtil.gson().fromJson(json, ObjWithTransient.class);
Assert.assertEquals(obj2.name, "Foobar");
Assert.assertNotNull(obj2.listOStrings); // <--- Fails here
Assert.assertEquals(obj2.listOStrings.size(), 0);
}
By marking it transient, I assume I am telling GSON to ignore it, but that doesn't seem to be the case. What is the best way to retain the initial settings here?
EDIT:
I believe the issue is because there is not a declared constructor. This does not work with an inner class, but with a normal class or a static inner class it appears to work. Reading the GSON code, it trys multiple ways to create the object, but ultimately uses a UnsafeConstructor to create it if nothing else works. This creates an object with null entries across the board. I could also add an InstanceCreator to tell Gson how to create the object.
I believe the issue is because there is not a declared constructor. This does not work with an inner class, but with a normal class or a static inner class it appears to work. Reading the GSON code, it trys multiple ways to create the object, but ultimately uses a UnsafeConstructor to create it if nothing else works. This creates an object with null entries across the board. I could also add an InstanceCreator to tell Gson how to create the object.
Is there a way in Java to iterate through the attributes of an object? Just like this:
class SomeClass {
private Object field_1;
private Object field_2;
...
private Object field_n;
// methods here
}
where o.field(i) refers to o.field_i?
Maybe I misunderstood you question but if you want to manipulate JSON and Java objects you should consider using library like Genson or Jackson.
If you really want to inspect your class' attributes (which you should not really be considered because it is not really clean coding) you can consider reflection as indicated by Andremoniy
EDIT :
I'm sorry, as Vogel612 said I'll explain.
After reading the post comments, you seems to receive message on REST WebServices in JSON that is then converted to Java Object.
Correct me if I'm wrong but I think you want want to manipulate you Java object and obtain JSON or the other way round without coding everything by hand.
If it is what you need, you can consider the library I cited earlier and I would add Gson from Google. These libraies provide several method to convert automatically Java object to JSON and JSON to Java object without laborious conversion coding.
Of course you can, but it is not recommended, avoid at all cost if possible.
This code :
import java.lang.reflect.Field;
public class JavaApplication24 {
public Integer x;
public Integer y;
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
JavaApplication24 app = new JavaApplication24();
app.x = 10;
app.y = 20;
for (Field f : app.getClass().getDeclaredFields()) {
System.out.println(f.getName());
System.out.println(f.getType());
System.out.println(f.toGenericString());
System.out.println(f.get(app));
}
}
}
Have this output
x
class java.lang.Integer
public java.lang.Integer javaapplication24.JavaApplication24.x
10
y
class java.lang.Integer
public java.lang.Integer javaapplication24.JavaApplication24.y
20
Google reflection for more info.
You can use Apache commons BeanUtils to get all properties and their values.
Please refer describe() from PropertyUtils or PropertyBeanUtils class.
http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.2/apidocs/org/apache/commons/beanutils/PropertyUtils.html#describe(java.lang.Object)
E.g.
TestBean bean = new TestBean();
bean.setId(1);
bean.setName("test");
bean.setLastAccessed(new Date());
System.out.println(PropertyUtils.describe(bean));
Sure, you can via reflection. But it is not really efficient way for dealing with object's fields:
public static Object getFieldValue(SomeClass someClass, int i ) {
try {
Field declaredField = SomeClass.class.getDeclaredField("field_" + i);
declaredField.setAccessible(true);
Object value = declaredField.get(someClass);
declaredField.setAccessible(false);
return value;
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
I try to use gson library to deserialize a flow of objects sent to me.
In all examples i've seen, when the method fromJson is called, we already know what type of object we expect to have.
In my case, I receive a flow of different objects and i'd like to know the best way to know the classes of objects before deserialize them.
{ A : {...}, B : { B1 : {...}, B2 : {...} }, C : {...} }
In this example, I'd like to have a way to know that 3 objects have been sent to me : A.class, B.class and C.class
Thanks
The documentation contains examples of deserializations using arbitrary classes or in two passes (first general deserialization in a collection, then content deserialization).
This exemple looks exactly like what you need. You could adapt it to use
JsonObject obj = parser.parse(json).getAsJsonObject();
to get a JsonObject instead of an array so that you can iterate on all properties (using entrySet) and deserialize according to the names (a = gson.fromJson(myjsonelement, A.class);) by simply mapping names to classes.
Yeah i too stumbled upon this issue. There is no way gson can figure out actual class of a field value. It simply tries to instantiate class used to define the field. Needless to say it is often not what we want.
so if you had, say
class C {
private A a;
private A c;
}
class B extends A {
}
then at runtime you
C c;
c.a = new B();
c.c = new B();
after deserialisation what you get is
c.a.getClass()==A.class;
c.b.getClass()==A.class;
so you would have to specify the subclass explicitly. Here is a wrapper class that is gson friendly.
public class S<T> {
private String objectClass;
private String rawObjectRepresentation;
// Gson needs no args constructor
public S() {
}
public S(T obj) {
objectClass = obj.getClass().getName();
rawObjectRepresentation = getGson().toJson(obj);
}
#SuppressWarnings("unchecked")
public T extract() throws ClassNotFoundException {
final Class<?> clazz = Class.forName(objectClass);
return (T)getGson().fromJson(rawObjectRepresentation, clazz);
}
private Gson getGson() {
return new GsonBuilder().create();
}
#Override
public String toString() {
return "type:"+objectClass;
}
}
If there is a field on the json object that you can use to identify the subclass you need to use, then you can use Gson on Fire: https://github.com/julman99/gson-fire
It has a feature called Type Selector that does exactly what you need.
Imagine a Base class and two child classes, A and B, then the code would look like this:
GsonFireBuilder builder = new GsonFireBuilder()
.registerTypeSelector(Base.class, new TypeSelector<Base>() {
#Override
public Class<? extends Base> getClassForElement(JsonElement readElement) {
String kind = readElement.getAsJsonObject().get("kind").getAsString();
if(kind.equals("a")){
return A.class; //This will cause Gson to deserialize the json mapping to A
} else if(kind.equals("b")) {
return B.class; //This will cause Gson to deserialize the json mapping to B
} else {
return null; //returning null will trigger Gson's default behavior
}
}
});
Gson gson = builder.createGson();
I am trying to get a list of objects from the Database based on a target object through reflection.
I don't have much experience with reflection so this doesn't work.
Is it even possible to achieve my goal?
public static List<Object> getObjectsFromDataBase(Object targetObj) {
....................
ResultSet rs = stat.executeQuery(queryToExecute);
while (rs.next()) {
Object obj = new Object();
for (Field property : targetObj.getClass().getFields()) {
property.set(obj, rs.getString(property.getName()));
}
objListToReturn.add(obj);
}
.....................
return objListToReturn;
}
If the fields in the target object's class are not public they won't show up in getFields(). Try this:
import java.lang.reflect.Field;
public class Test
{
public static class A
{
public String field1;
public String field2;
}
public static void main(String[] args) throws Exception
{
A a = new A();
Object b = a;
for (Field f : b.getClass().getFields())
{
System.out.println(f.getName());
}
}
}
If you remove public, getFields() returns an empty array.
The short answer is yes, this is possible, but not with the code you provided. This concept is called Object Relational Mapping and things like Hibernate or XStream do this for you. If you're just learning about reflection that's great too. You'll probably need a way to map the fields to the properties of the object, whether that's done with convention (matched names), straight code, annotations, or a mapping input file is up to you.
I can see two problems:
You are creating obj as type Object. the first argument of Field.set() needs to be the same type (or a subtype) as the class the containing the field. You need to use reflection to create an object of the same type as targetObj, by using obj = targetObj.getClass().newInstance() (assuming a default constructor is available)
Also your problem might be that the getFields() method only returns the public fields of the class.
targetObj.getClass().getFields()
If you want to get all the fields of the class, you will need to use the getDeclaredFields() method.
I'm using Google's GSON Library to convert JSON data to Java objects. The issue is that class name of the Java objet is being passed in the JSON data as well, so it is available only as a string. I'm not too familiar with Java so I don't understand how to declare an object when the class name is stored as a string.
Also, the same is the case for the method that I will be calling after the data is initialized.
Here's the relevant code so far:
request = gson.fromJson( rawData.toString(), JSONRequest.class );
String method = request.getMethod();
String data = request.getData();
String dataClass = request.getDataClass();
// convert data into an object of dataClass and execute method by passing dataObject
dataClass dataObject = gson.fromJson( data, dataClass.class );
result = method( dataObject );
This seems like a very crude way to accomplish the data to object conversion. Is there a better way? If not, how can I fix the code?
With the example code in the original question, dataClass.class will always be String.class, which I doubt is the correct target type to deserialize to.
I think the following is what was intended to be demonstrated. (I'm not sure what implementation of JSONRequest was used in the original question. I'll just make one up.)
If the target type is really not known, and it's not a sub-type of some known parent type, then reflection would be necessary to invoke the method. (This assumes that the target method does not require any parameters.)
import java.io.FileReader;
import java.lang.reflect.Method;
import com.google.gson.Gson;
public class Foo
{
public static void main(String[] args) throws Exception
{
Gson gson = new Gson();
// input JSON:
// {
// "dataClass":"com.stackoverflow.q6647866.MyObject",
// "method":"myMethod",
// "data":"{\"name\":\"fubar\"}"
// }
JSONRequest request = gson.fromJson(new FileReader("input.json"), JSONRequest.class);
Class targetClass = Class.forName(request.dataClass);
System.out.println("targetClass: " + targetClass);
Object dataObject = gson.fromJson(request.data, targetClass);
Method method = targetClass.getMethod(request.method);
method.invoke(dataObject);
}
}
class JSONRequest
{
String method;
String data;
String dataClass;
}
class MyObject
{
String name;
public void myMethod()
{
System.out.println("running my method...");
}
}
This seems like a very crude way to accomplish the data to object conversion. Is there a better way?
That's about as good as it gets, if the target data type and method to invoke are both completely unknown and only provided in the incoming JSON.