Java: get Map from Object - java

Using this code
Map<String,Object> payloadMap = new HashMap<String,Object>();
payloadMap = (Map<String,Object>) new Gson().fromJson(result, payloadMap.getClass());
, I convert this json:
{
"name":"name1",
"job":"prosecutor",
"department": {
"department_name":"prosecutor's office"
}
}
to the map (map with unlimited number of child maps):
This done well, but now I want to get an access to values of child (nested) maps.
In parent map child maps "wrapped" to Object.
So, I tried to get "wrapped" child maps from the Object-values of parent map.
public void mapRequestNode (Map<String,Object> payloadMap) {
payloadMap.entrySet().forEach(node->this.getDataFromNode(node));
}
As you can see from the above picture, there are no way to use child map "department", which had been "wrapped" to Object. I have an access to Object-methods, but not to the Map-methods (for example, I cant use "value.get("department_name")". I tried cast "(Map<String, Object>)value", but without success...
The "department" name in case above is only for example! I dont know concrete name of json child-objects. There may be unlimited number of names! So I cant use something like this "payloadMap.get("department")"

Following
((Map<String, Object>)payloadMap.get("department")).get("department_name")
should work, dont?

Your variable value is of type Object, which means that the compiler will not know anything else about the variable. Even if the object you retrieve from your json file is a map, as you store it in a Object variable, the compiler will handle it as an Object and not as a Map. That is why you cannot do value.get("department"); : the method get does not exist for the type Object.
You need to cast whatever is stored in value.get("department") as a Map<String, Object> to be able to handle it as a Map.

I have found a special solution.
I convert json to Map<String,Map<String,Object>>.
Not in Map<String,Object>. In this case I can successfully use child-maps of parent dto-map.
But this solution is special (not general) in the meaning that, I can handle in this way json, which consist only of objects.
For example, if I try to get the value of "job" in following example:
{
"job": "prosecutor",
"department": {
"department_name":"prosecutor's office"
}
}
by using Map.Entry<String, Map<String, Object>> payloadNodeEntry.getValue,
I will receive ClassCastException (cant cast String to Map<String, Object> ).

Related

Serialize and Deserialize arbitrary value using Jackson

I have a request json in the format:
{
"key1": "value1",
"key2": "value2",
.
..
...
"keyn": "valuen",
"generic": {
"key1":"string-type-value1"
"key2":"string-type-value2"
"key3":"complex-type-value3"
.
..
...
"keyn": "simple/complex-valuen"
}
}
As we can see, there is a property called generic .This property was initially made to accept arbitrary key-value pairs in String format only. Hence,we created a property Map<String,String>
The future requirement is such that we should also be able to accept arbitrary complex type value(such as list,array,etc) and not only String.
Is there a less-complex way to serialize/deserialize for this use-case using Jackson?
This property was initially made to accept arbitrary key-value pairs in String format only. Hence, we created a property Map<String, String>. [...] we should also be able to accept arbitrary complex type value(such as list,array,etc) and not only String.
Use a Map<String, Object> instead.

How to get Nested Gson keys (not values of keys) from Hashmap

My requirement is collect both the keys and values of keys from json and print it in list view as key value,so there will be no pre defined keys.
I have a nested object called as ListDetailModel, in that there are several other objects of other Pojo classes.
Right now I am able to derive the keys of parent object that is ListDetailModel.
My question is how to derive the keys of nested Pojos from the same entry set object. What I know is I can make separate objects for the required Pojo classes and derive keys.But can I do it from same class?
Map<String,ListDetailModel> result = gson.fromJson(response , Map.class); //result.get("").getExcavatorInformation().getClass()
//noinspection HardCodedStringLiteral
ListDetailModel model = new ListDetailModel();
for (Map.Entry<String,ListDetailModel> entry : result.entrySet()) {
String key = entry.getKey();
Log.d("++++++++++++++"," " +key);
// do stuff
}
So lets say there is another CarDetailModel inside ListDetailModel, cannot I do something like entry.get("CarDetailModel").getKey() and retrieve those keys ?
Map.Entry<String,ListDetailModel> is a node of the Map<String,ListDetailModel>.
You can only call getKey() or getValue() on it, not get("CarDetailModel").
If you want to query a value in a Map by Key, then you have to call the function get(Key k) on the map itself, not on one of its Entries
Use GSON by Google rather than parsing yourself
For Demos: https://github.com/google/gson

Reading attribute and values into map when key contains another key

I've been using Jackson for a while to parse json files and load the attribute and value into a Map. This is essentially what my code looks like:
Map<String, String> map = new HashMap<String, String>();
ObjectMapper mapper = new ObjectMapper();
File file = new File(pathToSource);
map = mapper.readValue(file, new TypeReference<HashMap<String, String>>() {});
This has worked well for flat json files where the keys are flat, just containing attribute/value pairs.
{
"attr":"value"
"attr":"value"
...
}
Now one of my sources has begun putting a key inside another key and th readValue method pukes when it hits the inner key.
{ "key1":{
"attr":"value"
"attr":"value"
"key2":{
"attr":"value"
"attr":"value"
}
}
}
One caveat of my need is I want to capture the attribute name and the value both. If I parse the json more granularly, taking attributes one by one, I can't access the attribute name.
I've been looking at this for a bit now and can't find the right combination to parse the keys, while capturing the attribute name and value.
Any suggestions welcome.
One thing to note is that if you just used simpler version:
Map<String, Object> map = mapper.readValue(file, Map.class);
you would get a Map that contains Strings, Lists and Maps as values, corresponding to matching JSON Structure (String, Array, Object).
You can generally use type java.lang.Object to mean "use the matching basic Java type", so signature you are asking for is Map<String,Object>, unless you want to enforce specific value, or use a POJO type.

Dynamically entering class literal as method argument at runtime

I have an application which makes use of an external library (Jackson), and the method I need requires a class literal as an argument. So if I wish to parse my JSON string into a User object:
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("user.json"), User.class);
Now, I wish to use this method dynamically (i.e. parse different JSON strings using the same line of code). For example:
String json1 = "{"type":"jacket",...}";
String json2 = "{"type":"sweater",...}";
Object object = mapper.readValue(json1/json2, ???);
//returns a Jacket object OR Sweater object based on the "type" key
//i.e. use Jacket.class as the 2nd argument if "type" is "jacket"
//OR Sweater.class if "type" is "sweater"
//After getting the deserialized object,
//if object is Jacket, cast as a Jacket
//if object is Sweater, cast as a Sweater
Of course, the JSON string in question can be for any class, so I can't simply hard-code an if-else loop. I've looked at custom serializers, but frankly am quite lost at what it's talking about, and would like some help in how I can go about this.
In summary, I need some way to first define a class literal from a String, and then cast the resulting Object into the specific class (but my focus is on getting readValue to work dynamically).
Looks like you need a mapping somewhere between JSON type variable and Java class type.
Generally result should be something like this map:
Map<String, Class<? extends YourSupertype>> map = new HashMap<>();
map.put("sweater", Sweater.class);
map.put("jacket", Jacket.class);
Just store possible clothing types somewhere in a file, then do something like:
String clothingType = nextEntryFromFile();
String className = constructClassNameFromClothingType(clothingType);
map.put(clothingType, Class.forName(className));
Since version 1.5 Jackson supports Polymorphic Type Handling, check here http://www.cowtowncoder.com/blog/archives/2010/03/entry_372.html
there are examples on how to correctly handle deserialization in those cases.

Parsing JSON into Map<String, Entity> with FlexJSON

I am trying to parse a JSON structure similar to this one:
{
"cars": {
"112": {
"make": "Cadillac",
"model": "Eldorado",
"year": "1998"
},
"642": {
"make": "Cadillac",
"model": "Eldorado",
"year": "1990"
},
"9242": {
"make": "Cadillac",
"model": "Eldorado",
"year": "2001"
}
}}
I have a CarEntity class defined with makeName,model,year attributes defined and accessible via setters/getters.
I am trying to deserialize this JSON like this:
Map<String, CarEntity> deserialized = new JSONDeserializer<Map<String, CarEntity>>()
.use("cars.values", Map.class)
.deserialize(json);
and it doesn't work :( It does deserialize it but not into Map<String, CarEntity> but rather into deep Map(something like Map<String, Map<String, Map<String, String>>> )
What am I doing wrong?
You're problem is your json has two maps. One which contains the 'cars' key, and one that contains the actual CarEntity. Unfortunately, you can't refer to a single key within a Map and assign types on just that key at this time. Generally setting types on values for collections refers to all values within the collection. You don't need to specify the types for the first Map that contains the "cars" key since it will deserialize it by default.
Map<String, CarEntity> deserialized = new JSONDeserializer<Map<String,Map<String, CarEntity>>>()
.use("values.values", CarEntity.class )
.deserialize(json).get("cars");
The path 'values.values' refers to the outer Map's values then traversing the next map values are all CarEntity instances.
I've considered changing the path expressions to be more expressive allowing you to target a single value in a collection, but this increases overhead of evaluating them and being backwards compatible is a challenge.
You are most likely being bitten by Java Type Erasure: JSON library in question does not know type you want; all it sees is equivalent of Map. So you must specify at least value type somehow. Hopefully FlexJSON documentation points out how.
Alternatively you may be able to sub-class HashMap into your own type (MyEntityMap extends HashMap), since then type information can be inferred from generic super type; and passing MyEntityMap.class would give type information that most JSON libraries can use (Jackson and GSON at least).
If these do not work, Jackson and GSON libraries can handle this use case easily; both have methods to specify generic types for deserialization.
Just add one more call to get("cars") like:
Map<String, CarEntity> deserialized = new JSONDeserializer<Map<String, CarEntity>>()
.use("cars.values", Map.class)
.deserialize(json).get("cars");
jSon string was probably serialized from a variable cars typed as Map<String, CarEntity>

Categories

Resources