I am playing with flexjson and Google Cloud Endpoints. My model which I need to serialize is:
public class SampleModel {
Long id;
DateTime createdAt;
String message;
OtherModel other;
}
I just created DateTimeObjectFactory to find a way of creating DateTime objects (lacks of no arg constructor). Now I have question about OtherModel and SampleModel as well.
I want to serialize in fact a List of SampleModel. So here is my code:
List<SampleModel> sampleList = new ArrayList<SampleModel>();
// ...
// adding some items to sampleList
// ...
String s = new JSONSerializer().deepSerialize(sampleList);
I want to deepSerialize it for now to avoid some unserializated fields, but just for now.
When I want to deserialize s I do that:
sampleList = new JSONDeserializer<List<SampleModel>>()
.use("other", OtherModel.class)
.use(DateTime.class, new DateTimeObjectFactory())
.deserialize(s);
I think that everything is just ok in that kind of deserializing, because I can see in logs deserialized object. But in fact when I want to get item from that new sampleList I get an error:
java.lang.ClassCastException: java.util.HashMap cannot be cast to com.test.games.testapi.model.SampleModel
If I have good understanding every not trivial object will be deserialized as Map if I don't point the right class to deserializer. So this error means that script didn't know SampleModel? What is the meaning of this?
Related
I have an object which has a map inside it:
MyDTO
HashMap<Integer>
Now when I convert my MyDTO to JSON (with Gson), and then back from JSON to MyDTO what I get is HashMap<String>.
I convert from JSON back to object like this:
MyDTO dto = gson.fromJson(json, MyDTO.class);
How can I force it to convert/keep the Map inside the DTO as Map<Integer> and NOT as Map<String>?
Here is my Object:
public class MultiSeriesTimebasedChartDTO implements Serializable
{
LinkedHashMap<String, ArrayList<Number>> data;
}
Here's how I convert my JSON back to object:
multiSeriesTimebasedChartDTO = gson.fromJson(json, MultiSeriesTimebasedChartDTO.class);
And here is the result (in screenshot), which were Numbers but now are Strings. I needed them back as Numbers.
So looking for a clean approach for this.
I can definitely iterate over it, change every number from string back to number, and replace it... But I was thinking may be there is some other better way of oing it.
The values are still java.lang.Numbers after the JSON is parsed. However, because your field has the type LinkedHashMap<String, ArrayList<Number>>, Gson uses its internal type LazilyParsedNumber because it cannot know as which specific type you want the Numbers to be parsed. LazilyParsedNumber acts as a wrapper for the JSON string representation so you can call the respective Number method to parse the value.
LazilyParsedNumber should suffice if you are only retrieving its intValue(), doubleValue() ..., but if want to compare it with other Numbers it wont work since LazilyParsedNumber is only equal to itself.
Since your question mentions that the Map contains Integer values, the easiest solution would be to change the type of the DTO field:
LinkedHashMap<String, ArrayList<Integer>>
This way Gson knows the exact number type you want and can properly deserialize the JSON numbers as Integers.
You have no "HashMap<Integer>" whatever that could be, you have ArrayList<Number>, and that is what GSON has to prepare for:
public class MultiSeriesTimebasedChartDTO implements Serializable{
LinkedHashMap<String, ArrayList<Number>> data;
^^^^^^^^^^^^^^^^^
}
Also, you don't have Strings what you complain about, those are LazilyParsedNumbers,
And while it really stores the value as a string, that class indeed is a Number. You don't have to worry about its private member variables.
public final class LazilyParsedNumber extends Number { // <= extends Number
private final String value; // <= this is what you see in the debugger
But that is just the explanation about what's there now. If you want GSON to produce you a list of Integers, you should simply write that:
public class MultiSeriesTimebasedChartDTO implements Serializable{
LinkedHashMap<String, ArrayList<Integer>> data;
}
remember that GSON can only analyse the declaration of the class, it can't guess if you later ensure that all those generic numbers are integers.
My Flink pipeline currently uses an Pojo that contains some Lists and Maps (of Strings), along the lines of
public class MyPojo {
private List<String> myList = new ArrayList<>();
private OtherPojo otherPojo = new OtherPojo();
// getters + setters...
}
public class OtherPojo {
private Map<String, String> myMap = new HashMap<>();
// getters + setters...
}
For performance reasons, I want to get around Kryo serialization, so I disabled the generic fallback with env.getConfig().disableGenericTypes(); as described in the Flink documentation.
Now, Flink complains about the lists:
Exception in thread "main" java.lang.UnsupportedOperationException: Generic types have been disabled in the ExecutionConfig and type java.util.List is treated as a generic type.
at org.apache.flink.api.java.typeutils.GenericTypeInfo.createSerializer(GenericTypeInfo.java:86)
at org.apache.flink.api.java.typeutils.PojoTypeInfo.createPojoSerializer(PojoTypeInfo.java:319)
at org.apache.flink.api.java.typeutils.PojoTypeInfo.createSerializer(PojoTypeInfo.java:311)
at org.apache.flink.streaming.api.graph.StreamGraph.addOperator(StreamGraph.java:258)
at org.apache.flink.streaming.api.graph.StreamGraphGenerator.transformOneInputTransform(StreamGraphGenerator.java:649)
at org.apache.flink.streaming.api.graph.StreamGraphGenerator.transform(StreamGraphGenerator.java:250)
at org.apache.flink.streaming.api.graph.StreamGraphGenerator.generate(StreamGraphGenerator.java:209)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.getStreamGraph(StreamExecutionEnvironment.java:1540)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.execute(StreamExecutionEnvironment.java:1507)
...
What is the preferred way of serializing such simple lists and maps in Flink?. Internally, these are currently ArrayList and HashMap, but other implementations would also be fine. There seems to be a class org.apache.flink.api.common.typeutils.base.ListSerializer in Flink, but I do not know how to use it.
Marius already explained the reason beautifully, although I don't see the reason why Flink does not support your use case out of the box. Nevertheless, I'll add the solution that works right now.
// create type info
final TypeInformation<OtherPojo> otherPojoInfo = Types.POJO(OtherPojo.class,
ImmutableMap.of("myMap", Types.MAP(Types.STRING, Types.STRING)));
final TypeInformation<MyPojo> myPojoInfo = Types.POJO(MyPojo.class,
ImmutableMap.of("myList", Types.LIST(Types.STRING), "otherPojo", otherPojoInfo));
// test it
final MyPojo myPojo = new MyPojo();
myPojo.getMyList().add("test");
myPojo.getOtherPojo().getMyMap().put("ping", "pong");
final TypeSerializer<MyPojo> serializer = myPojoInfo.createSerializer(env.getConfig());
DataOutputSerializer dataOutputSerializer = new DataOutputSerializer(100);
serializer.serialize(myPojo, dataOutputSerializer);
DataInputDeserializer dataInputDeserializer = new DataInputDeserializer(dataOutputSerializer.getSharedBuffer());
final MyPojo clone = serializer.deserialize(dataInputDeserializer);
assert(myPojo.equals(clone));
Note that the terrible access pattern in the test code is just for a quick and dirty demonstration.
If you do:
env.getConfig().disableGenericTypes();
It will raise an exception whenever a data type is encountered that would go through Kryo.
So in that case you have to write your own Serializer.
Which can be created using TypeSerializer, simply call typeInfo.createSerializer(config) on the TypeInformation object.
For generic types, you need to “capture” the generic type information via the TypeHint, in your case for a list:
TypeInformation<List<Object>> info = TypeInformation.of(new TypeHint<List<Object>>(){});
ListTypeInfo class
More details in here.
I am getting object in the response of entityManager.find method.
and i want to get values from that object by passing key. but i din't get any success.
For example :-
my entity :-
#entity
class Test (){
public Long id;
public String name ;
public String descr;
}
and i am getting object in the response of below code.
`Object obj=`entitymanager.find(classname,id);
Note :- Instead of object i can't use entity's object directly because input class name can be dynamically pass that's why i am taking response in Object.
Now i want to get value from object by passing key
something like that obj.getvalue("id");
I tried below things to make it done :-
Map<String, Object> user = (Map<String, Object>)obj;
Used json simple parser to parse it.
JSONParser parser = new JSONParser();
JSONObject jsonObject =parser.parse(obj.toString());
But din't get any success.
Please help me out.
I'm a little unsure of what you're asking... but as long as "name" is a primary key you can call
Test obj = entitymanager.find(Test.class, name);
Object obj=entitymanager.find(classname,id);
In the above line what is the type of "classname".
Since your question does not mention the type. I assume its of type Object.
You should do something like
Object obj=entitymanager.find(classname.getClass(),id);
I'm trying to deserialize objects from JSON file.
I have abstract class Car, which is extended by Minibus and Minivan classes.
I determine the exact class by values of maxPassengers and trunkSize fields.
I need to create a MinibusCar or MinivanCar object when I deserialize based on the parameters in JSON. What is the most efficient way to do that? Please advice.
There is not enough information here. Can you show us an example of this json file?
In general, you can use Jackson package:
// Serialize into JSONObject
JSONObject json = (JSONObject) JSONSerializer.toJSON(jsonTxt);
// Get your value from it, for example:
int maxPassengers = json.get("maxPassengers");
// Init your object mapper
ObjectMapper mapper = new ObjectMapper();
// Based on this value, create your object
Car car; // Using polymorphism (you can also read about Factory design pattern)
if (maxPassengers > 10){
car = mapper.readValue(jsonTxt, MinibusCar.class);
} else {
car = mapper.readValue(jsonTxt, MinivanCar.class);
}
But this solution isn't perfect until you will show us the classes (three of them) and some json example file.
Good luck!
I have to convert a POJO to JSON object but i am encountering a problem.
My POJO has four variables and their getters and setters.
private String RouteCoord;
private String RouteName;
private String freqArray;
private Double AvgTime;
I have declared a list of objects
List<KPIsStructure> listOfKPIsObjects = new ArrayList<>();
I run a for loop in which i create instances of my POJO class and add them to the already declared list (listOfKPIsObjects)
for (int i=0;i<routes.length;i++){
KPIsStructure innerObject = new KPIsStructure();
innerObject.setRouteCoord(routes[i][1]);
innerObject.setRouteName(routes[i][0]);
innerObject.setAvgTime(AvgTime[i]);
innerObject.setfreqArray(freqArray[i]);
listOfKPIsObjects.add(innerObject);
}
Then there is this another for loop to make a nested JSON Object
for (int i=0;i<listOfKPIsObjects.size();i++){
JSONObject temp = new JSONObject(listOfKPIsObjects.get(i));
processedJson.put("journey_"+(i+1), temp);
}
I return processedJson. The problem is the result is missing one of the variables (String freqArray) of the POJO class.
{"journey_1":{"avgTime":12.283334,"routeCoord":"435,345-789,1011","routeName":"tty-keskustori"},"journey_2":{"avgTime":12.283334,"routeCoord":"123,456-789,1011","routeName":"hervanta-tty"},"journey_3":{"avgTime":12.283334,"routeCoord":"123,456-789,1011","routeName":"kaleva-keskustori"}}
I have tried passing the variables of the class as string and it works and it also shows that the string freqArray is not empty.
String toJson = "{\"RouteName:"+listOfKPIsObjects.get(i).getRouteName()+",RouteCoordinates:"+listOfKPIsObjects.get(i).getRouteCoord()+",AvgTime:"+listOfKPIsObjects.get(i).getAvgTime()+",VisitFrequency:"+listOfKPIsObjects.get(i).getfreqArray()+"}";
Doing it this way takes away the idea of mapping from POJO to JSON object and vice versa and makes it inconvenient.
Any ideas what i might be doing wrong here. I am using org.json.