I am trying to deserialize JSON objects in my Java application using gson
.
However, I cant figure how to dynamically apply the class type.
I have the class-name in a String
User user = gson.fromJson(userJSON, User.class);
How can I put String myClass as class type?
I believe what is suggested in the previous answer is perfectly OK. Just to avoid all confusions and make it much obvious, here is the full code:
package com.test;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
public class TestGSON {
public static void main(String[] args) {
Gson gson = new Gson();
try{
Object fromJson = gson.fromJson("{\"name\":\"Dharam\"}", Class.forName("com.test.MyClass"));
if(fromJson instanceof MyClass){
System.out.println(fromJson);
}
} catch (JsonSyntaxException | ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class MyClass {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "MyClass [name=" + name + "]";
}
}
Output from the above code:
MyClass [name=Dharam]
Class<?> cls = Class.forName(className);
But your className should be fully-qualified - i.e. com.mycompany.MyClass
Related
We have a class Agent with assignedUsers as List<Long>
when we try to convert the object as JSON document, we are using ObjectMapper writeValueAsString method, which does not serialize the ids into String instead the key assignedUsers are not missed in the JSON string.
import java.io.IOException;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Agent {
private List<Long> assignedUserIds;
private String name;
private static final String json = "{\"name\":\"New Agency\", \"assignedUserIds\":[23,24]}";
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List<Long> gocuetAssignedUserIds() {
return assignedUserIds;
}
public void setAssignedUserIds(final List<Long> assignedUserIds) {
this.assignedUserIds = assignedUserIds;
}
public static void main(String[] args) {
Agent agencyInfo = null;
try {
agencyInfo = new ObjectMapper().readValue(json, Agent.class);
System.out.println("Built Agent :: " + new ObjectMapper().writeValueAsString(agencyInfo)); // Outputs: Built Agent :: {"name":"New Agency"}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Your getter for the field assignedUserIds is called gocuetAssignedUserIds(). This is why it's not serialized (the mapper does not recognize it), either mark it with the annotiation #JsonGetter("assignedUserIds") or rename it to getAssignedUserIds() to match the field.
I am trying to access the method GetDatbaseName(), from the returned object obj, but it is returning error that the method is not available.
However, when I Typecast the obj, it is working.
String name = ((Oracle)obj).GetDatabaseName();
How to handle this generic? Like I can't typecast for each return type like Oracle and MongoDB. Also any better implementation for this?
// one class needs to have a main() method
public class HelloWorld
{
// arguments are passed using the text field below this editor
public static void main(String[] args)
{
Data dt = new Data("Oracle");
Object obj = dt.GetObject();
String name = obj.GetDatabaseName();
System.out.println(name);
}
}
public class Data
{
public String _type;
public Data(String type)
{
_type = type;
}
public Object GetObject()
{
Object obj = null;
switch(_type)
{
case("Oracle"):
obj = new Oracle("Test");
break;
case("MongoDB"):
obj = new MongoDB("TestCollection");
break;
}
return obj;
}
}
public class Oracle
{
public String _databaseName;
public Oracle(String databaseName)
{
_databaseName = databaseName;
}
public String GetDatabaseName() { return _databaseName; }
}
public class MongoDB
{
public String _collectionName;
public MongoDB(String collectionName)
{
_collectionName = collectionName;
}
public String GetCollectionName() { return _collectionName; }
}
There are two ways to solve this, the first is using a generic class, while the second is using interface, the second approach is better if you know that the classes will have the same methods, while the generic approach is if the classes have different methods
Generic approach
public class DBtest{
public static void main(String[] args){
DataBase<Oracle> database = new DataBase<>(Oracle.class);
Oracle oracle = database.getDataBase();
System.out.println(oracle.getDatabaseName());
}
}
class DataBase<T>{
private T database;
public DataBase(Class<T> classOfT){
try {
database = classOfT.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public T getDataBase(){
return database;
}
}
class Oracle{
private String _databaseName;
public Oracle(){
_databaseName = "test";
}
public String getDatabaseName() { return _databaseName; }
}
As you can see, it is not possible to define the name of the database, this would be possible of you write <T extends Name> which is an interface which has getName() and setName() method
Interface approach
public class DBtest{
// arguments are passed using the text field below this editor
public static void main(String[] args){
DataBase database = new DataBase(new Oracle("test"));
DatabaseName databaseName = database.getDataBase();
System.out.println(databaseName.getName());
}
}
interface DatabaseName {
String getName();
}
class DataBase{
private DatabaseName databaseName;
public DataBase(DatabaseName databaseName){
this.databaseName = databaseName;
}
public DatabaseName getDataBase(){
return databaseName;
}
}
class Oracle implements DatabaseName {
private String _databaseName;
public Oracle(String name){
_databaseName = name;
}
public String getName() {
return _databaseName;
}
}
class MongoDB implements DatabaseName {
private String _databaseName;
public MongoDB(String name){
_databaseName = name;
}
public String getName() {
return _databaseName;
}
}
Obviously DatabaseName is a bad name for an interface, but it is the only method which is the same for both classes, so it makes sense to call it that. The great thing about interfaces is that you don't have to give a shit about what class is used as long as you know the method names.
You problem is on the following lines:
Object obj = dt.GetObject();
String name = obj.GetDatabaseName();
As far as those lines are concerned, obj is of type Object, which does not have the invoked method; thus, the issue. This is due to Java being strongly typed.
To go around that, you need a type that has this method, or use reflection. To use a type that has this method, they need to inherit it from a common parent of implement it from a common interface. You can also wrap you objects or a bunch of other alternatives.
In your case, it seems that a common interface is the easiest way to go. In this case, each class should implement this interface and instead of using Object your reference would be of the type of that interface.
public Object GetObject()
Would become
public MyInterface GetObject()
and
public class Oracle
would be
public class Oracle implements MyInterface
Where MyInterface would declare the method
public interface MyInterface {
String GetDatabaseName();
}
Being mindful of Java conventions, methods should start with lowercase
public interface MyInterface {
String getDatabaseName();
}
In the case where you cannot change the code in order to implements those methods, you can use "instanceof" to test against the class type.
name = (obj instanceof Oracle)?((Oracle)obj).GetDatabaseName():((MongoDB )obj).getCollectionName();
You must have to create an Interface and then with getDatabaseName() method. Then your objects Oracle and MongoDB must implement that interface.
What you are trying to do is something similar to AbstractFactory Pattern. You should google it.
public interface MyDbInterface {
String getDatabaseName();
}
public class HelloWorld {
// arguments are passed using the text field below this editor
public static void main(String[] ){
MyDbInterface dt = DataFactory.create("Oracle");
String name = dt.getDatabaseName();
System.out.println(name);
}
}
public final class DataFactory{
private DataFactory(){
super();
}
public static MyDbInterface create(String type){
MyDbInterface obj = null;
switch(type) {
case("Oracle"):
obj = new Oracle("Test");
break;
case("MongoDB"):
obj = new MongoDB("TestCollection");
break;
}
return obj;
}
}
public class Oracle implement MyDbInterface{
public String databaseName;
public Oracle(String databaseName){
databaseName = databaseName;
}
#Override
public String getDatabaseName() {
return databaseName;
}
}
public class MongoDB implement MyDbInterface{
public String collectionName;
public MongoDB(String collectionName){
collectionName = collectionName;
}
public String getCollectionName() {
return collectionName;
}
#Override
public String getDatabaseName() {
return getCollectionName();
}
}
I suposed you come from C#, check java style guide. ;)
You should think about the design of your code. You need to use basic OOP principal to solve the problem. There are several ways to solve your problem like using interface/generics etc. Here I am giving one such example.
public class HelloWorld {
public static void main(String[] args) {
Data dt = new Data("Oracle");
DataBase obj = dt.GetObject();
String name = obj.getDatabaseName();
System.out.println("Name : "+name);
}
}
class Data {
public String _type;
public Data(String type) {
_type = type;
}
public DataBase GetObject() {
DataBase dataBase=null;
switch (_type) {
case "Oracle":
dataBase = new Oracle();
break;
case "Mongo":
dataBase = new MongoDb();
break;
}
return dataBase;
}
}
interface DataBase {
String getDatabaseName();
}
class Oracle implements DataBase {
public String getDatabaseName() {
return "Oracle";
}
}
class MongoDb implements DataBase {
public String getDatabaseName() {
return "Mongo";
}
}
Edited:
Here is another way to solve your problem. I believe this approach might solve your problem.
public class HelloWorld {
public static void main(String[] args) {
Data<Oracle> dt = new Data<Oracle>("Oracle");
Oracle obj = dt.getObject();
String name = obj.getDatabaseName();
System.out.println("Name : "+name);
}
}
class Data<T> {
public String _type;
public Data(String type) {
_type = type;
}
public T getObject() {
Object dataBase=null;
switch (_type) {
case "Oracle":
dataBase = new Oracle();
break;
case "Mongo":
dataBase = new MongoDb();
break;
}
return (T)dataBase;
}
}
class Oracle {
public String getDatabaseName() {
return "Oracle";
}
}
class MongoDb {
}
Situation: few apps communicate using Java DTOs.
I have classes which holds as its fields another classes and they hold another another classes (up to three levels down from top DTO).
Fields could be single DTO or as (exclusively) ArrayList of other classes (DTOs).
All classes are DTO. Just private fields and public setters and getters.
Now, when I get top DTO is there any way to inspect it and get all getters, including nested ones, read fields through getters and then do what I have to do (change some data, specifically remove/change some characters (I have method which does that, all final fields are eventually Strings or Integers), and then write data back using appropriate setter. I guess the best would be to find getter/setter pair per final field and do operation then move to next. Upon finding final (lowest level field) I should check if it is String (do the operation) and if Integer skip operation.
I know there is similar question but it doesn't deal with nested DTOs.
Java reflection get all private fields
If possible I would avoid any 3rd party library.
Any advice on this?
UPDATE: Almost there. Here is kind of demo code, I wish it is so simple, but conceptually it is more less like that:
class SymposiaDTO
import java.util.ArrayList;
public class SymposiaDTO {
private ProgramDTO programDTO;
private ArrayList<PaperDTO> papersDTO;
public ProgramDTO getProgramDTO() {
return programDTO;
}
public void setProgramDTO(ProgramDTO programDTO) {
this.programDTO = programDTO;
}
public ArrayList<PaperDTO> getPapersDTO() {
return papersDTO;
}
public void setPapersDTO(ArrayList<PaperDTO> papersDTO) {
this.papersDTO = papersDTO;
}
}
class ProgramDTO
public class ProgramDTO {
String programTitle;
Integer programID;
public String getProgramTitle() {
return programTitle;
}
public void setProgramTitle(String programTitle) {
this.programTitle = programTitle;
}
public Integer getProgramID() {
return programID;
}
public void setProgramID(Integer programID) {
this.programID = programID;
}
}
class PaperDTO
import java.util.ArrayList;
public class PaperDTO {
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public ArrayList<AuthorDTO> getAuthrosDTO() {
return authrosDTO;
}
public void setAuthrosDTO(ArrayList<AuthorDTO> authrosDTO) {
this.authrosDTO = authrosDTO;
}
private String title;
private ArrayList<AuthorDTO> authrosDTO;
}
class AuthorDTO
public class AuthorDTO {
private String address;
private String name;
private String title;
private String age;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
class Controller <--- by Carlos if I got his instructions right, this version gives no output at all, never even get's single iteration in for loop.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Controller {
#SuppressWarnings({ "unused", "rawtypes" })
public static void main(String[] args) {
SymposiaDTO symposiaDTO = new SymposiaDTO();
ProgramDTO programDTO = new ProgramDTO();
PaperDTO paperDTO = new PaperDTO();
AuthorDTO authorDTO = new AuthorDTO();
Class<?> topClass = symposiaDTO.getClass();
for (Class<?> innerClass : topClass.getDeclaredClasses()) {
for (Field field : innerClass.getDeclaredFields()) {
if (Modifier.isPrivate(field.getModifiers())) {
String name = Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
Method getter;
try {
getter = innerClass.getDeclaredMethod("get" + name);
} catch (Exception ex) {
getter = null;
}
Method setter;
try {
setter = innerClass.getDeclaredMethod("set" + name, field.getType());
} catch (Exception ex) {
setter = null;
}
// TODO real work...
System.out.printf("%s: getter=%s, setter=%s%n", innerClass.getSimpleName(), getter, setter);
}
}
}
}
}
class Controller2 <--- slightly modified previous version, this gets into the loop, but runs twice, and it never gets deeper into nested DTOs.
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Controller2 {
#SuppressWarnings({ "unused", "rawtypes" })
public static void main(String[] args) {
SymposiaDTO symposiaDTO = new SymposiaDTO();
ProgramDTO programDTO = new ProgramDTO();
PaperDTO paperDTO = new PaperDTO();
AuthorDTO authorDTO = new AuthorDTO();
Class<?> topClass = symposiaDTO.getClass();
List<Class> classesToWalk = new ArrayList<Class>();
for (Field field : topClass.getDeclaredFields()) {
Class symposiaDTO2 = field.getDeclaringClass();
classesToWalk.add(symposiaDTO2);
}
for (Class<?> innerClass : classesToWalk) {
Field[] fields = Arrays.stream(innerClass.getDeclaredFields())
.filter(field -> Modifier.isPrivate(field.getModifiers())).toArray(Field[]::new);
for (Field field : fields) {
String name = Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
Method getter;
try {
getter = innerClass.getDeclaredMethod("get" + name);
} catch (Exception ex) {
getter = null;
}
Method setter;
try {
setter = innerClass.getDeclaredMethod("set" + name, field.getType());
} catch (Exception ex) {
setter = null;
}
// TODO real work...
System.out.printf("%s: getter=%s, setter=%s%n", innerClass.getSimpleName(), getter, setter);
}
}
}
}
This is output from Controller2:
SymposiaDTO: getter=public ProgramDTO SymposiaDTO.getProgramDTO(),
setter=public void SymposiaDTO.setProgramDTO(ProgramDTO)
SymposiaDTO: getter=public java.util.ArrayList
SymposiaDTO.getPapersDTO(), setter=public void
SymposiaDTO.setPapersDTO(java.util.ArrayList)
SymposiaDTO: getter=public ProgramDTO SymposiaDTO.getProgramDTO(),
setter=public void SymposiaDTO.setProgramDTO(ProgramDTO)
SymposiaDTO: getter=public java.util.ArrayList
SymposiaDTO.getPapersDTO(), setter=public void
SymposiaDTO.setPapersDTO(java.util.ArrayList)
You could use getDeclaredClasses to find nested classes, then find the private fields and finally the getters and setters:
Class<?> topClass = ...
for (Class<?> innerClass : topClass.getDeclaredClasses()) {
for (Field field : innerClass.getDeclaredFields()) {
if (Modifier.isPrivate(field.getModifiers())) {
String name = Character.toUpperCase(field.getName().charAt(0))
+ field.getName().substring(1);
Method getter;
try {
getter = innerClass.getDeclaredMethod("get" + name);
} catch (Exception ex) {
getter = null;
}
Method setter;
try {
setter = innerClass.getDeclaredMethod("set" + name, field.getType());
} catch (Exception ex) {
setter = null;
}
// TODO real work...
System.out.printf("%s: getter=%s, setter=%s%n",
innerClass.getSimpleName(), getter, setter);
}
}
}
Edit: above code is valid for "nested classes" as mentioned in the questions title. After the sample code was added to the question it seems like the question is about getters and setters of the fields of the class:
Use getDeclaredFields to get all fields of the class and find the corresponding getter and setter as above; use getType to get the type (class) of each field and (recursively) start over with that class.
I'm trying to create an object using reflection like this
(maybe this is not a good architecture but I'm just doing some tests)
package com.interfaces;
public interface IEmployee {
List<entities.Employee> getEmployees();
}
then
package com.personnel;
public class Employee implements IEmployee{
public Employee(){}
public List<entities.Employee> getEmployees(){
...
}
}
then in another class
package com.factory;
import java.lang.reflect.Constructor;
public final class EmployeeFactory {
private static String path = "com.personnel";
public EmployeeFactory() {}
public static interfaces.IEmployee CreateEmployee(){
String className = path + ".Employee";
try {
Class<?> cls = Class.forName(className);
Object object = cls.newInstance();
return (interfaces.IEmployee)object;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
}
then in another class
public final class PersonnelService {
private static interfaces.IEmployee employeeFactory = com.factory.EmployeeFactory.CreateEmployee();
public List<entities.Employee> getEmployees(){
return employeeFactory.getEmployees();
}
}
finally in my main method
public static void main(String[] args) {
List<entities.Employee> employees = new com.services.PersonnelService().getEmployees();
for(entities.Employee employee : employees){
System.out.println(employee.getEmployeeName());
}
when the code arrives to Class cls = Class.forName(className) it throws the exception message "com.personnel.Employee.(java.lang.String)" with "NoSuchMethodException".
UPDATE
I found the problem. In fact, it was a bad naming convention. I had a serializable Employee class (my pojo) and an Employee class which implements my interface IEmployee which is intended to perform DB operations. I renamed this last one as EmployeeDAL and it worked. Even I defined the full class name as you can see in the example, it looks like that I was trying to instantiate my serializable pojo (I still don't get it, but...). Thanks for your time.
I tried to reproduce your code and not found any error on this code. I only get NoSuchMethodException message when I change Employee constructor with String argument
package com.agit.example.test;
import java.util.ArrayList;
import java.util.List;
public class TestClass {
public static void main(String[] args) {
List<Employee> employees = new PersonnelService().getEmployees();
for(Employee employee : employees){
System.out.println(employee);
}
}
}
interface IEmployee {
List<Employee> getEmployees();
}
class Employee implements IEmployee{
public Employee(){}
public List<Employee> getEmployees(){
List<Employee> x = new ArrayList<>();
x.add(this);
return x;
}
}
final class EmployeeFactory {
private static String path = "com.agit.example.test";
public EmployeeFactory() {}
public static IEmployee CreateEmployee(){
String className = path + ".Employee";
try {
Class<?> cls = Class.forName(className);
Object object = cls.newInstance();
return (IEmployee)object;
} catch (Exception e) {
System.out.println(e.getMessage());
return null;
}
}
}
final class PersonnelService {
private static IEmployee employeeFactory = EmployeeFactory.CreateEmployee();
public List<Employee> getEmployees(){
return employeeFactory.getEmployees();
}
}
I'm having some issues to deserialize a Json array that follows this format:
[
{
"ChildList":[
{
"ChildList":[
],
"Id":110,
"Name":"Books",
"ApplicationCount":0
}
],
"Id":110,
"Name":"Books",
"ApplicationCount":0
}
]
It's basically an array of Categories where each category can also have a List of sub-categories, and so on and so on.
My class model looks a little like this:
public class ArrayOfCategory{
protected List<Category> category;
}
public class Category{
protected ArrayOfCategory childList;
protected int id;
protected String name;
protected int applicationCount;
}
Now, Gson obviously complains about the circular reference. Is there any way to parse this Json input given that I can't assume how many levels of categories there are?
Thanks in advance.
Edit:
Just in case someone has a similar problem, based on Spaeth answer I adapted the solution to a more general case using reflection. The only requirement is that the List of objects represented by the JSON array is wrapped in another class (like Category and ArrayOfCategory in my example). With the following code applied to my original sample, you can just call "deserializeJson(jsonString,ArrayOfCategory.class)" and it will work as expected.
private <T> T deserializeJson(String stream, Class<T> clazz) throws PluginException {
try {
JsonElement je = new JsonParser().parse(stream);
if (je instanceof JsonArray) {
return deserializeJsonArray(clazz, je);
} else {
return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create().fromJson(stream, clazz);
}
} catch (Exception e) {
throw new PluginException("Failed to parse json string: " + ((stream.length() > 20) ? stream.substring(0, 20) : stream) + "... to class " + clazz.getName());
}
}
private <T> T deserializeJsonArray(Class<T> clazz, JsonElement je) throws InstantiationException, IllegalAccessException {
ParameterizedType listField = (ParameterizedType) clazz.getDeclaredFields()[0].getGenericType();
final Type listType = listField.getActualTypeArguments()[0];
T ret = clazz.newInstance();
final Field retField = ret.getClass().getDeclaredFields()[0];
retField.setAccessible(true);
retField.set(ret, getListFromJsonArray((JsonArray) je,(Class<?>) listType));
return ret;
}
private <E> List<E> getListFromJsonArray(JsonArray je, Class<E> listType) {
Type collectionType = new TypeToken<List<E>>(){}.getType();
final GsonBuilder builder = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
Gson jsonParser = builder.create();
return jsonParser.fromJson(je, collectionType);
}
Maybe you could try this:
com.google.gson.Gson gson = new GsonBuilder().create();
InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("/tmp/gson.txt")));
Collection<Category> fromJson = gson.fromJson(reader, new TypeToken<Collection<Category>>() {}.getType());
System.out.println(fromJson);
you will get a good result.
The "magic" occurs here: new TypeToken<Collection<Category>>() {}.getType()
The entire code is:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.List;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
public class GsonCircularReference {
public class Category {
protected List<Category> childList;
protected int id;
protected String name;
protected int applicationCount;
public List<Category> getChildList() {
return childList;
}
public void setChildList(final List<Category> childList) {
this.childList = childList;
}
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public int getApplicationCount() {
return applicationCount;
}
public void setApplicationCount(final int applicationCount) {
this.applicationCount = applicationCount;
}
#Override
public String toString() {
return "Category [category=" + childList + ", id=" + id + ", name=" + name + ", applicationCount="
+ applicationCount + "]";
}
}
public static void main(final String[] args) throws JsonSyntaxException, JsonIOException, FileNotFoundException {
com.google.gson.Gson gson = new GsonBuilder().create();
InputStreamReader reader = new InputStreamReader(new FileInputStream(new File("/tmp/gson.txt")));
Collection<Category> fromJson = gson.fromJson(reader, new TypeToken<Collection<Category>>() {}.getType());
System.out.println(fromJson);
}
}
JSON file is:
[
{
"childList":[
{
"childList":[
],
"id":110,
"Name":"Books",
"applicationCount":0
}
],
"id":110,
"name":"Books",
"applicationCount":0
}
]
Take a look at GraphAdapterBuilder. You'll need to include it in your app, but it can serialize arbitrary graphs of objects.