Reading attribute and values into map when key contains another key - java

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.

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

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

PropertyNamingStrategy ignored when using #JsonAnyGetter

Scenario:
Using Jackson 2.4.5 I have a dynamic bean to be serialised into JSON which can store some of its state in 'optional' properties in an internal map and uses #JsonAnyGetter on an accessor method that returns this map, e.g:
public class DynamicJsonView {
private final Map<String, Object> optionalProperties = new HashMap<>();
private final String rqdProperty = "blah";
public String getRqdProperty() {
return rqdProperty;
}
public DynamicJsonView() {
optionalProperties.put("PROP_1", "value 1");
optionalProperties.put("PROP_2", "value 2");
optionalProperties.put("PROP_3", "value 3");
// etc - in reality populated from another map
}
#JsonAnyGetter
public Map<String, Object> any() {
return Collections.unmodifiableMap(optionalProperties);
}
}
Note the map keys are UPPER_CASE. When we setup our ObjectMapper we set the following naming strategy to convert properties to lower case (and replace camelCase with snake_case), e.g:
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
Problem:
This works exactly as expected with normal java properties, i.e. rqdProperty in the example above converts to rqd_property in the JSON serialized form, but the naming strategy is seemingly ignored for the map 'properties', with the upper case keys appearing unmodified. Having debugged the jackson LowerCaseWithUnderscoresStrategy#translate method and watched the input parameter values passed in as the object is serialised, it seems the keys are never passed through the naming strategy.
The obvious workaround is to pre-process the map keys and convert them all to lower case, but I wondered if there's something I'm missing with regards to the property naming strategy, or if this is simply a limitation of the library?
This is as designed since NamingStrategy is only applied to actual concrete properties, and not for Map keys, or "any" properties.
But if ability to include name mangling for any properties sounds like a good idea, you could request a new feature to do that: it could be enabled (for example) by a flag of #JsonAnySetter:
https://github.com/FasterXML/jackson-databind/issues/

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