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

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

Related

Java: get Map from Object

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> ).

Java - Get all local field values of an object as map

I want to retrieve a map of field-values present in a java object. I tried com.google.Gson and org.apache.commons.lang3.builder.ToStringBuilder to retrieve all the details regarding the given object. But following problems occured :
com.google.Gson:
Gson gson = new GsonBuilder.create();
String str = gson.reflectionToString(myObject);
StackOverFlowError occurs for some objects, even a map having JSONObject fails miserably.
org.apache.commons.lang3.builder.ToStringBuilder:
String str = ToStringBuilder.reflectionToString(myObject);
While using the above API for HashMap, variables inside the HashMap are displayed but contents of the HashMap are not visible.
Output :
samplePackage.CustomHashMap#5fec459b[accessOrder=false,threshold=12,loadFactor=0.75]
Help me to find the perfect solution !
You could try using reflection. This allows you to use your class name to access your class and its meta data. You can then use the getDeclaredFields method to get all the declared Fields for your class. Your fields can be private this way so they can't be edited by outside classes without calling getters or setters.
//initialize the name of your class.
String className = "examplePackageName.exampleClassName";
//get your class using the name of the class
Class<? extends ObjectImplementation> Classtouse = Class.forName(className);
//initialize map of fieldnames to field objects from reflection
Map<String,Field> FieldMap = new HashMap<>();
//gets delcared fields as a List, converts it to an array and for each element in the
//array, it maps the element's name to the element itself as a field object
Arrays.asList(Classtouse.getDeclaredFields()).forEach(Field -> FieldMap.put(Field.getName(),Field));
//now you have your fieldmap ready to use.
when you want to access the value of a Field, you can use
//instantiate an object of your Class with type Classtouse here
//use the object created in the line above and set its fields using the line
//below
field.get(//Object that holds this field);
Where field is the Field object you want to edit which you accessed using the fieldname or some other method. If you want to map fieldnames to field values directly you can edit this from the code above:
Arrays.asList(Classtouse.getDeclaredFields()).forEach(Field -> FieldMap.put(Field.getName(),Field));
to be:
Arrays.asList(Classtouse.getDeclaredFields()).forEach(Field -> FieldMap.put(Field.getName(),Field.get(//your object here)));
Just make sure you have instantiated an object you want to do this with first.
An example of this and other reflection methods can be found in the Field javadoc
Here:[link]https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Field.html
This code is not garenteed to be perfectly runnable but it's close.

Redis data retrieval

I have a map in redis cache with the following structure :
Cache entry : Map<String, Map<String, String>>
The inner map could have as many as 25000 elements/buckets.
Is there a way in redis to retrieve a specific value from the inner map based on the keys(redis key, outer map's key and inner map's key) without having to fetch the entire redis entry in my Java method that accesses this redis cache?
Currently, if I have to delete a particular key from the inner map, am doing it the following way :
Map<String, Map<String, String>> mapFromRedis = redis.get("myRediscacheKey");
Map<String, String> innerMap = new HashMap<>();
if (!mapFromRedis.isEmpty()) {
innerMap = mapFromRedis.get("key");
}
if (innerMap.containsKey("keyToBeDeleted")) {
innerMap.remove("keyToBeDeleted");
mapFromRedis.put("key", innerMap);
}
redis.set("myRediscacheKey", mapFromRedis);
Your data structure has 3-level mapping: Redis key, outer map key, and inner map key. From your example, we can get the following mapping: myRediscacheKey -> key -> keyToBeDeleted -> value
However, Redis can only has at most 2-level mapping, i.e. HASH. In order to achieve your goal, you have to do compress the 3-level mapping to a 2-level mapping, or even 1-level mapping.
Compress to 2-level mapping
You can combine the Redis key with your outer map key as the key of a HASH, and take the inner map key as the field of the HASH. In that way, we can get the 2-level mapping as follows: myRediscacheKey:key -> keyToBeDeleted -> value.
// set inner map key
hset myRediscacheKey:key keyToBeDeleted value
// remove inner map key
hdel myRediscacheKey:key keyToBeDeleted
Compress to 1-level mapping
You can also combine the 3 keys into one key: myRediscacheKey:key:keyToBeDeleted -> value
// set inner map key
set myRediscacheKey:key:keyToBeDeleted value
// remove inner map key
del myRediscacheKey:key:keyToBeDeleted
You can easily do it with Redisson. It allows to reference Redis object to another Redis object though special reference objects which handled by Redisson. Here is the code sample:
RMap<String, RMap<String, String>> mapFromRedis = redisson.getMap("myRediscacheKey");
// Redisson loads only small reference object
// to another Redis map and NOT entire map
Map<String, String> innerMap = mapFromRedis.get("key");
if (innerMap.remove("keyToBeDeleted") != null) {
// key has been deleted
}
// or with faster approach
if (innerMap.fastRemove("keyToBeDeleted") == 1) {
// key has been deleted
}
That's it! You don't need to execute huge update on each inner map change. Under the hood it works using only 2 operations over Redis: get and hdel
RMap extends standard Java Map and ConcurrentMap interfaces. Thus you don't need to handle with keys/connections/serialization. This all done by Redisson.

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.

Copy HashMap of different type

How can I copy content of one HashMap<String,AddressDTO> to another HashMap<String,AddressBO> of a different type. There is no
inheritance between AdressDTO and AddressBO ,both are POJOs with the same set of attributes:
AddressDTO addDTO = new AddressDTO();
addDTO.setAdd1("add1");
addDTO.setAdd2("add2");
addDTO.setAddtype("pri");
addDTO.setCity("city");
Map<String,Object> map1 = new HashMap<String,Object>();
map1.put("primary", addDTO);
Map<String,Object> map2 = new HashMap<String,Object>(map1);
AddressBO addnew = (AddressBO) map2.get("primary");
//this will give me runtime error AddressDTO cannot be cast to AddressBO
System.out.println(addnew.getAdd1());
System.out.println(addnew.getAdd2());
System.out.println(addnew.getAddtype());
You can loop through the results of entry set which will give you the key value pairs and allow you to copy.
Also, HashMap takes a map. Probably other maps do too.
You cannot cast one Object to another based on field similarity only. You should use inheritance, or use some kind of transformer to create AddressBO from AddressDTO
If both Object contains exactly the same fields, there is no need for two classes.
There are various tools available that will use reflection to copy the values.
Alternatively you could just set the reference in the new map to be the same as the old map (then they will both share the same HashMap).
But it sounds like your real problem is mapping from the DTO to the BO.
For example if you create a constructor for the BO that accepts a DTO and creates a new BO from it then you could just do:
for (Entry<String, DTO> e: map1) {
map2.put(e.getKey(), new BO(e.getValue()));
}

Categories

Resources