Gson converts the HashMap<Integer> to HashMap<String> inside my Object - java

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.

Related

How to get the values of specific fields in a Map<String, Object>

I have something like:
Map<String, Object> hashMap;
When I do something like:
hashMap.get("binary"), I get value as: {size=5642, name=abc}
Here key is binary and value is an Object of Type Object and is {size=5642, name=abc}
Note the values dont belong to a particular class.
In Python I can do something like hashMap["binary"]["size"], was wondering what would be the equivalent in java
How do I get the value of size directly without parsing the above string?
The value is not of Type Object, but of some type that extends from Object (in java everything extends Object implicitly). Let's call it "X"
Now, it doesn't work like python because unlike python java doesn't have that dynamic nature.
{size=5642, name=abc} is probably a string representation of that type X. This is what you see in a debugger or maybe when trying to print the value on console with System.out.println or something.
Now first of all figure out which type is it:
Object value = hashMap.get("binary")
System.out.println(value.getClass().getName());
It will print the class name
Then check the source of that class, probably it looks like this:
public class X {
private final int size;
private final String name;
... // constructor, other stuff maybe
// these are called "getters" in java world
public int getSize() {return size;}
public String getName() {return name;}
}
From that point you have 2 ways to get the size:
Object value = hashMap.get("binary");
int size = ((X)value).getSize(); // This is called downcasting
The drawback of this method is that you don't utilize the power of generics
So the better option is a refactoring if its possible of course:
Map<String, X> hashMap = ...
X value = hashMap.get("binary");
value.getSize();
One final note:
If it happens that the value is of type String, you won't be able to get the size other than parsing the value with regular expression or something. In this case consider a refactoring as a better option.

Get Class Name for the given String

I have a use case, where I have stored the List of Java Data Types in DB, Like Byte, Character, Integer, Long, BigDecimal, BigInteger, Boolean.
So my use case is like If I read the value Long, I need to create the Long.class, if I read String, then String.class.
Class cls = Class.forName("java.lang.Long);, then I can use the cls for my own purpose.
I can achieve this, by having Enum of the above data types, as soon I read the value from the db, I pass the value to enum to get the class type. But I don't know whether it is efficient or not. Is there any method present in Java which gives like, for the given string,(without fully qualified name), it should return the class type.
Storing a reference to the Class object is efficient but using the Class object for reflection can be expensive. If you're just using the Class for reference then you're fine.
enum Decodable {
BIG_INTEGER(BigInteger.class),
INTEGER(Integer.class)
// etc
private final Class<?> decodableClass;
private Decodable(Class<?> decodableClass) {
this.decodableClass = decodableClass;
}
}
You could also just maintain a Set of Class objects.
private static final Set<Class<?>> DECODABLE_CLASSES = ImmutableSet.of(Integer.class, BigInteger.class); //etc

How can I deserialize a list of enums using Jackson JSON?

I'm working on a configuration system. I'd like to be able to load config values from a JSON file and have them "automagically" convert to the Java type I need. I'm using Jackson for the JSON parsing. For primitive types like floats and strings, it's no big deal, but I'm running into a snag with enums.
Let's say I have the following enum:
public enum SystemMode
{
#JsonProperty("Mode1")
MODE1("Mode1"),
#JsonProperty("Mode2")
MODE2("Mode2"),
#JsonProperty("Mode3")
MODE3("Mode3");
private final String name;
private SystemMode(String name)
{
this.name = name;
}
#Override
#JsonValue
public String toString()
{
return this.name;
}
}
Now, let's say I want to represent a list of values of this enum for a given config variable using the following JSON representation:
{
"Project" : "TEST",
"System" : {
"ValidModes" : ["Mode1", "Mode2"]
}
}
And I'd like to be able to do something like the following:
ArrayList<SystemMode> validModes = (ArrayList<SystemMode>) configurator.getConfigValue("/System/ValidModes");
For reference, my configurator class's getConfigValue method is essentially a thin wrapper over the Jackson JSON parsing:
public Object getConfigValue(String JSON_String)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(JSON_String);
return objectMapper.convertValue(node, Object.class);
}
(The real method has some exception checking that has been omitted for clarity).
Now, when I call the above, Jackson correctly deduces that I want an ArrayList and fills it. However, instead of getting an ArrayList of SystemMode enums, I get an ArrayList of Strings and immediately throw an exception when I attempt to use the list. I have tried several different ways of representing the data to no avail. It seems no matter what I try, Jackson wants to return a list of strings instead of a list of enums.
So my question is this:
How can I make Jackson (version 2.9.4) JSON properly deserialize a list of enum values in a way that is compatible with my single "Object getConfigValue()" method?
The following will provide the correct binding for your enum.
public List<SystemMode> getConfigValue(String path)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(path);
return objectMapper.convertValue(node, new TypeReference<List<SystemMode>>(){});
}
The second option is to convert the list of String yourself, for example:
List<SystemMode> result = jsonResult.stream().map(SystemMode::valueOf).collect(Collectors.toList());
Third option:
public <T>List<T> getConfigValue(String path, Class<T> type)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(path);
CollectionType toType =
objectMapper.getTypeFactory().constructCollectionType(List.class, type);
return objectMapper.convertValue(node, toType);
}

serialization object reference with Gson

I'm working on a map builder application.
It have bus, train, stations, and lines between stations.
All those data are stored in a class called 'Map'.
I need to store my Map instance into a JSON file. I choose to use Gson.
Here's my Map, Line and Station classes:
public class Map
{
private LinkedList<Station> stations;
private LinkedList<Line> lines;
// methods ...
}
public class Line
{
private String name;
private LinkedList<Station> stations;
// methods
}
public class Station
{
private int number;
private String name;
private double latitude;
private double longitude;
private String cityName;
// methods
}
My problem is than all my object are store as independent object (without any reference).
As you can see map contains stations and lines. And lines contains stations.
When I load the JSON, and change a station in the map class, the same station doesn't change because it's not linked by reference.
Take a look at this one.
http://code.google.com/p/json-io/
See if it preserves your 'graph of objects' semantics.
Also look at:
http://benjamin.smedbergs.us/blog/2008-08-21/json-serialization-of-interconnected-object-graphs/
http://www.jspon.org/#JSPON%20Core%20Spec
I am not really sure JSON in its original form supports what you want (and you want to
serialize and then properly deserialize a whole graph of objects, not just a tree of objects).
I guess JSON does not support that but I am not 100% sure (I haven't needed that myself).
In a tree each object has just 1 parent, so there you don't have issues.
In a graph you may have objects A and B both pointing to some object C.
So how do you serialize C now? Do you do it in A (i.e. while serializing A) or in B, or outside of both A and B, and then have A and B point to that single serialized C somehow? Or do you just serialize C twice (once in A and once in B)? Btw, isn't it the
same with XML? I guess both XML and JSON are not designed for this.

Define generic/variable type for data structure

I am having a data structure (LinkedHashMap) but the problem is that the (second) value should be of variable type since I can put there type String or type int or any primitive type so my question is:
Is there a way to define a variable type for it that can get any value type?
This is what I'm having:
private LinkedHashMap<String, String> keyVal;
I want something like this:
private LinkedHashMap<String, anyValue> keyVal;
private LinkedHashMap<String, Object> keyVal;
You can use Object for that. But do remember that while trying to get data back from this map(sometime later), you may face difficulty in casting the Object to your required data type, as you may not know, what data type is actually present.
Hence, its advisable to avoid such implementations.
You cannot have a generic type be a primitive type. If you want to be able to store anything in your map, you can have the "value" generic type for the map be Object:
private LinkedHashMap<String, Object> keyVal;
You can still store what looks like primitives types due to autoboxing, i.e.
keyVal.put("one", 1);
will place an Integer, even though you specified an int.
No, the closest you can have is Object as a second argument.
Now, I would advise to rethink what you need to accomplish, since this is actually going against what generics were created for.
If you have a bound type and want to maintain some flexibility, then you could use something like <String, ? extends SomeType>.
Mixing several types of Objects in the same data-structure is not advisable in Java (if this is good or bad, is beside the point), but type safety goes a long way in preventing weird errors along the line.
Try to think about how you would deal with this when you actually need to retrieve the objects... will you assume they're Strings? What are you going to do with them?
You say you want to have a Map< String, Primitive type>.
A specified by the JLS, primitives are NumericType or boolean, NumericType are IntegralType or FloatingPointType.
If your need is not primitive but only NumericType, you may use java.lang.Number:
Map< String, Number >
Another way is to define a class Any which hold all the possible attributes:
enum Type {
NULL,
INTEGER,
SHORT,
FLOAT,
...
}
class Any {
private int iValue;
private short sValue;
private float fValue;
...
private Type active = Type.NULL;
public void setInt( int value ) {
iValue = value;
active = Type.INTEGER;
}
public void setFloat( float value ) {
fValue = value;
active = Type.FLOAT;
}
...
public int getInt() {
if( type != Type.INTEGER ) {
throw new ClassCastException( type.name() + " is not an integer" );
}
return iValue;
}
...
}
It's up to you to put some check and throw exception if getInt() is called on a float holder. Everything is possible, transtyping like C language for example.
EDIT
You want String too, and String isn't a primitive.
You have to add the following below private short sValue; into the Any class:
private String sValue;
and the followinf below SHORT, into the Type enum:
STRING,
But, like others says, the best way is to avoid these weak type (fourre-tout in french).
You can use
private LinkedHashMap<String, Object> keyVal;
to leave the second type argument as general as possible.
It allows you to store any object as a value, because every class extends Object.
This leads you to the problem that you don't know what type of things are inside your map - you only know that they are of type Object what means you don't know anything.
So to use these objects again you would have to cast them back to their original type what may cause a runtime exception: ClassCastException.
Generics are about defining data structures for different types with the same code, but if you want to use a generic class you have to parameterize it with its type arguments. This ensures that the type is known at runtime and is the great advantage of generics (avoid ClassCastException).
However, you can still specify a more general type that allows multiple types.
For example, if you define it the following way you can store any object that implements Serializable.
private LinkedHashMap<String, ? extends Serializable> keyVal;
As you can see, this allows you to restrict the permitted types to a common property (i.e., to be a subclass of a more general type). That way, you use the map's values as objects of the more general class, because it's everything you know (and want to know) about the objetcs.
It's better to have a look at:
Generics lesson on Oracle.com.
Care when should use wild cards (?) and you should use Generics.
Using Object in type of LinkedHashMap<String, Object> keyVal; is not recommended.
Like some people said, you could use Object for generic variable type, especially while using generic method or not knowing what data type user would come, like this simple one:
import java.util.Scanner;
public class GenericMethod {
public static void main(String[] args) {
System.out.println("Type something that's yours: ");
Scanner sc = new Scanner(System.in);
Object thing;
thing = sc.next();
isMine(thing);
}
// Generic Method
public static <T> void isMine(T x) {
System.out.println(x + " is mine.");
}
}

Categories