Cast base class to inherited class from deserialized collection in java - java

I have a base class
SocialRecord.java
public class SocialRecord{
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
and two derived classes
SocialEmailRecord.java
public class SocialEmailRecord extends SocialRecord{
private String subject;
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
SocialDiscussionRecord.java
public class SocialDiscussionRecord extends SocialRecord{
private String source;
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
}
I create some instances of SocialEmailRecord and SocialDiscussionRecord classes.
SocialEmailRecord r1 = new SocialEmailRecord();
r1.setSubject("sub1");
SocialEmailRecord r2 = new SocialEmailRecord();
r2.setSubject("sub2");
SocialDiscussionRecord r3 = new SocialDiscussionRecord();
r3.setSource("source3");
SocialDiscussionRecord r4 = new SocialDiscussionRecord();
r4.setSource("source4");
I add these objects to a collection.
List<SocialRecord> records = new ArrayList<>(Arrays.asList(r1, r2, r3, r4));
I serialize this collection by gson.
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String objsInJson = gson.toJson(records);
Now I would like to deserialize this collection, but I don't know how to construct original objects of collection.
Type listType = new TypeToken<ArrayList<Object>>() {}.getType();
List records = new ArrayList<>(Arrays.asList(graphUtils.getGson().fromJson(objsInJson), listType)));
I tried this, but it doesn't work.
for (int i = 0; i < records.size(); i++){
if (records.get(i).getClass().equals(SocialDiscussionRecord.class))
records.set(i, (SocialDiscussionRecord) records.get(i));
if (records.get(i).getClass().equals(SocialPhoneRecord.class))
records.set(i, (SocialPhoneRecord) records.get(i));
if (records.get(i).getClass().equals(SocialEmailRecord.class))
records.set(i, (SocialEmailRecord) records.get(i));
if (records.get(i).getClass().equals(SocialRecord.class))
records.set(i, (SocialRecord) records.get(i));
}

You will have to implement a TypeHierarchyAdapter and register it to the GsonBuilder that will manage the deserialisation of the specificities of each subclass.
See here for a full classical example with Employee/Manager
But then you will get an ArrayList<SocialRecord> even if the object instances are actual SocialEmailRecord or SocialDiscussionRecord. You need to define some methods in SocialRecord that shall be overridden in the subclasses and use polymorphism to process the list.

Related

Gson Deserializing Problem - If Type parameter is child class could not deserialize

In Master class for the variable Generic<Parent> generic i am passing a Child Object in main(). During serializing i am getting correct output. But while deserializing Child Object is missing. Could anyone give suggesstions.
public class GenericSample {
public static void main(String[] args) {
Generic<Parent> generic = new Generic<Parent>();
Child child = new Child();
child.setName("I am child");
generic.setT(child);
Gson gson = new Gson();
Master master = new Master();
master.setId(2);
master.setGeneric(generic);
String valMaster = gson.toJson(master);
System.out.println(valMaster);
/*
* Output: {"id":2,"generic":{"t":{"name":"I am child"}}}
*/
Master master2 = gson.fromJson(valMaster, Master.class);
String valMaster2 = gson.toJson(master2);
System.out.println(valMaster2);
/*
* Child Object is missing
* Output: {"id":2,"generic":{"t":{}}}
*/
}
static class Master {
private int id;
private Generic<Parent> generic;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Generic<Parent> getGeneric() {
return generic;
}
public void setGeneric(Generic<Parent> generic) {
this.generic = generic;
}
}
static class Generic<T> {
T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
static class Parent {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
static class Child extends Parent {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
Problem
Gson tries to deserialize the generic value into Parent, not Child. Since there is type as null, you can see no data in the object deserialized which appears as {}. If you add child.setType("type"); then the outputs become:
valMaster1: {"id":2,"generic":{"t":{"name":"I am child","type":"type"}}}
valMaster2: {"id":2,"generic":{"t":{"type":"type"}}}
However, the field name is not present in the Parent class but the Child class and Gson simply has no idea what subclass of Parent it is (if so) and completely ignores the value, which is a correct behavior.
Solution
I find basically two choices (I use all-args constructor for sake of brevity):
Elevate the upper-bounded generic type parameter to the Master class and specify the particular Child type at the point of deserialization using com.google.gson.reflect.TypeToken and java.lang.reflect.Type:
static class Master<T extends Parent> {
private int id;
private Generic<T> generic;
/* getters, setters and all-args constructor */
}
Child child = new Child("I am child");
Generic<Parent> generic = new Generic<>(child);
Master<Parent> master = new Master<>(2, generic);
Gson gson = new Gson();
String valMaster = gson.toJson(master);
System.out.println(valMaster);
// {"id":2,"generic":{"t":{"name":"I am child"}}}
Type type = new TypeToken<Master<Child>>() {}.getType();
Master<Child> master2 = gson.fromJson(valMaster, type);
String valMaster2 = gson.toJson(master2);
System.out.println(valMaster2);
// {"id":2,"generic":{"t":{"name":"I am child"}}}
Hardcode the particular generic type Generic<Child> inside the Master class. The deserialization gets the way easier, yet the design is less flexible:
static class Master {
private int id;
private Generic<Child> generic;
/* getters, setters and all-args constructor */
}
Child child = new Child("I am child");
Generic<Child> generic = new Generic<>(child);
Master master = new Master(2, generic);
Gson gson = new Gson();
String valMaster = gson.toJson(master);
System.out.println(valMaster);
// {"id":2,"generic":{"t":{"name":"I am child"}}}
Master master2 = gson.fromJson(valMaster, Master.class);
String valMaster2 = gson.toJson(master2);
System.out.println(valMaster2);
// {"id":2,"generic":{"t":{"name":"I am child"}}}

Java set values of list of custom object which is a inner class

Hi I am facing a problem while trying to set a list of custom class object which is inside another class.
public class Request {
private List<Custom> Custom;
public List<Request.Custom> getCustom() {
return Custom;
}
public void setCustom(List<Request.Custom> custom) {
Custom= custom;
}
public class Custom{
private String id;
public String getid() {
return id;
}
public void setid(String Id) {
id= Id;
}
}
}
Now how do I set the id from another class?
Use following syntax to create object of inner class :
classInstance.new InnerClass()
Then you can have a list of Custom from other class
Request req = new Request();
List<Request.Custom> clist = new ArrayList<>();
Request.Custom c;
c = req.new Custom();
c.setid("one");
clist.add(c);
c = req.new Custom();
c.setid("two");
clist.add(c);
req.setCustom(clist);
You have to do it so:
Request myR = new Request( );
Request.Custom myCustom = myR.new Custom();
myCustom.setid("17");
the trick here is myR.new Custom();
and this is the consequence because you are using inner/nested classes

Esper: Grammatically defining a type with sub types?

I have to define the class below in ESPER so I'm able to reference the sub-types and internal arrays. I have to do it pragmatically. I don't care how:
UPDATE: The complete class:
public class IoTEntityEvent implements java.io.Serializable {
private IoTProperty[] Properties;
private String About;
IoTEntityEvent (){
this.About = null;
this.Properties = null;
}
public String getAbout() {
return About;
}
public void setAbout( String value){
this.About = value;
}
public void setProperties(int index, IoTProperty value) {
Properties[index] = value;
}
public IoTProperty getProperties(int index) {
return Properties[index];
}
public void setProperties( IoTProperty[] value) {
Properties = value;
}
public IoTProperty[] getProperties() {
return Properties;
}
}
This is the sub-class:
public class IoTProperty implements java.io.Serializable {
private Map<String,String>[] IoTStateObservation =null;
private String About = null;
IoTProperty (){
this.About = null;
this.IoTStateObservation = null;
}
public String getAbout() {
return About;
}
public void setAbout(String value) {
About = value;
}
public Map<String,String>[] getIoTStateObservation() {
return IoTStateObservation;
}
public void setIoTStateObservation( Map<String,String>[] value) {
IoTStateObservation = value;
}
public Map<String,String> getIoTStateObservation(int index) {
return IoTStateObservation[index];
}
public void setIoTStateObservation(int index, Map<String,String> value) {
IoTStateObservation[0] = value;
}
}
I tried like this :
eventNames[0] = "About";
eventType[0] = String.class;
eventNames[1] = "Properties";
eventType[1] = IoTProperty[].class;
epService.getEPAdministrator().getConfiguration().addEventType("type", eventNames, eventType);
This works but I can't access the sub-types. I also tried to define the sub type in similar manner. Can someone can explain how I suppose to do it?
What do you mean with "This works but I can't access the sub-types."
Tried like "select Properties[0].whatever" from type?
According to the Esper documentation:
Plain-old Java object events are object instances that expose event properties through JavaBeans-style getter methods. Events classes or interfaces do not have to be fully compliant to the JavaBean specification; however for the Esper engine to obtain event properties, the required JavaBean getter methods must be present or an accessor-style and accessor-methods may be defined via configuration.
In short, you need to create the JavaBean getters and setters in order to access your private members.
Thank you for the help. I found out how and is as following:
epService.getEPAdministrator().getConfiguration().addEventType("type",IoTEntityEvent.class);
Then the event should be send like this without any casting:
IoTValue[] va= {new IoTValue("0.62","2014-06-09T18:08:40.968Z","2014-06-09T18:08:40.968Z")};
IoTProperty[] pr = {new IoTProperty(va,"property")};
IoTEntityEvent event = new IoTEntityEvent(pr,"Entity");
epService.getEPRuntime().sendEvent(event);

How to write a generic getObject() method for json string?

Got into a very basic issue. I have to convert json string to objects. I have a custom method as below which is expected to convert into corresponding class and throw an exception if it is not able to get the object out of it.
protected <T> T getObjectFromJson(Class<T> c, String json){
try{
Gson gson = new Gson();
T object = gson.fromJson(json, c);
return object;
} catch (Exception e){
throw new TMMIDClassConversionException(e.getCause(), e.getMessage());
}
}
The issue is this method is not throwing exception if I am trying to convert json of a different class.
My class
public class CompanyCategoryMap {
private Integer id;
private int mid;
private String catKey;
private String catValue;
private int priority;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getMid() {
return mid;
}
public void setMid(int mid) {
this.mid = mid;
}
public String getCatKey() {
return catKey;
}
public void setCatKey(String catKey) {
this.catKey = catKey;
}
public String getCatValue() {
return catValue;
}
public void setCatValue(String catValue) {
this.catValue = catValue;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
}
When I pass json string of Company rather than String of above class, it does not throw exception.
The string:
"{\"id\":6,\"name\":\"abc\",\"usersCount\":10,\"mid\":3,\"createdAt\":\"Sep 15, 2014 7:02:19 PM\",\"updatedAt\":\"Sep 15, 2014 7:02:19 PM\",\"active\":true,\"currency\":\"abc\",\"source\":\"unknown\",\"user_id\":1,\"tierId\":1}"
I think I am doing this conversion in a wrong way. What is the suggested way of doing it?
Take for example:
class Foo {
private String value;
}
class Bar {
private String value;
}
and
String json = "{\"value\" : \"whatever\"}";
new Gson().fromJson(json, Foo.class);
new Gson().fromJson(json, Bar.class);
Why should Gson reject any of these?
Gson is setup to perform a best effort to deserialize the given JSON into an instance of the given Class. It will map as many fields as it finds. If none are found, that's too bad.
Other libraries like Jackson do the opposite. By default, Jackson rejects any JSON which doesn't contain a mapping for every given class property. You can also configure it to ignore some properties.
Keep doing what you are doing. As the application writer, you should know when to use a Class instance with the appropriate JSON source.

How to deserialize JSON Array contained an abstract class without modifying a parent class?

I'm trying to deserialize JSON Array, which is persisted into my MongoDB, to a Java object by using Jackson. I found many tutorials mentioned to handle this polymorphism by adding:
#JsonTypeInfo(use=Id.CLASS,property="_class")
to a Super-class. However, in my case, I can't be able to modify the Super-class. So, are there some solutions to solve it without modifying the Super-class? Here is my code:
public class User {
#JsonProperty("_id")
private String id;
private List<Identity> identities; // <-- My List contains objects of an abstract class; Identity
public User(){
identities = new ArrayList<Identity>();
}
public static Iterable<User> findAllUsers(){
return users().find().as(User.class); // Always give me the errors
}
/*More code*/
}
It always give me the error - Can not construct instance of securesocial.core.Identity, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information.
You can use #JsonDeserilize annotation to bind a concrete implementation class to an abstract class. If you cannot modify your abstract class you can use the Jackson Mix-in annotations to tell Jackson how to find the implementation class.
Here is an example:
public class JacksonAbstract {
public static class User {
private final String id;
private final List<Identity> identities;
#JsonCreator
public User(#JsonProperty("_id") String id, #JsonProperty("identities") List<Identity> identities) {
this.id = id;
this.identities = identities;
}
#JsonProperty("_id")
public String getId() {
return id;
}
public List<Identity> getIdentities() {
return identities;
}
}
public static abstract class Identity {
public abstract String getField();
}
#JsonDeserialize(as = IdentityImpl.class)
public static abstract class IdentityMixIn {
}
public static class IdentityImpl extends Identity {
private final String field;
public IdentityImpl(#JsonProperty("field") String field) {
this.field = field;
}
#Override
public String getField() {
return field;
}
}
public static void main(String[] args) throws IOException {
User u = new User("myId", Collections.<Identity>singletonList(new IdentityImpl("myField")));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Identity.class, IdentityMixIn.class);
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(u);
System.out.println(json);
System.out.println(mapper.readValue(json, User.class));
}
}

Categories