How to serialize/deserialize a map with Solr/Lucene? - java

I am new to solr, and I am facing a problem when I try to serialize/deserialize a Map in Solr.
I use Spring Data Solr in my Java application as follow:
#Field("mapped_*")
private Map<String, String> values;
It flatten and serializes my map in Solr as follow:
"key1" : "value1"
"key2" : "value2"
...
However, when I run a search, the returned objects have this field always set as NULL. Deserialization does not work on this particular field, it looks like it does not recognize the key1, key2... as part of the Map.
Does anyone know how to make the derialization work? Do I have to implement a custom converter?

At this time Spring Data Solr does not automatically prefix values contained in the map with the given #Field#value, but will just use the Map#key as fieldname. There's an improvement (DATASOLR-202) open.
At this time having key1, key2,.. in values requires the fieldname to be key* in order to read back values correctly.
#Field("key*")
private Map<String, String> values;

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

Getting a map property from environment variable with Spring

I'm creating a microservice with Spring Boot that uses some properties loaded both with #Value and #ConfigurationProperties. Those properties will have default values defined in the application.yaml but I want to override them with environment variables. I've manage to do it with almost every basic property type but I'm unable with maps.
This is the code that retrieves the property, so the map is found in configuration.map:
#Data
#ConfigurationProperties(prefix = "configuration")
public class MapConfig{
private Map<String, Object> map;
}
The default values I have in the application.yaml are:
configuration:
map:
a: "A"
b: 2
c: true
This works fine and I get a map containing those key-value entries. Problem comes when I try to initialize the map with the environment variables. I've tried with multiples variants of CONFIGURATION_MAP='{aa:"aa", bb:12, cc:true}' and every time I start the application I get the default values defined in application.yaml but no trace of the environment map.
I also tried adding variables like CONFIGURATION_MAP_AA='HELLO' and I was able to put new values in my map. However all I can do is adding information but the default values I wrote in the yaml are still there. In this case I'm getting:
{
"aa": "HELLO",
"a": "A",
"b": 2,
"c": true
}
This isn't the behavior I'm looking for. I want to completely override the default map instead of adding new info. It also has the problem that keys added this way are always transformed to lowercase so I can't override keys with camelCase. And values are also casted to strings even when my map is <String,Object>.
Can anyone tell me how to properly initialize maps with environment variables or point me in the right direction? Thanks a lot!
The problem is the order in which the properties are been loaded.
Have a look at this doc that explains the loading order. I don't know if it will be
useful for your scenario but you can try creating a profile (for example application-other.yml) and load this. It will load after the application.yml
The cast is coming from the most general type in your list. In the end all three "name, 1, true" can be Strings. I can't think of a way of loading Object but you can recast your String on what you like ( if its needed. ) Else break it into multiple maps that are type specific ( Map<String,String> , Map<String,Boolean>, Map<String,Integer> )

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 the map values from "yml" file as a Map?

I have a "details.yml" file, considering all the setting for getting all values from "yml.file" is done. But I am unable to store Map values into
"Map"
Here is my "details.yml" file below
details:
company:XYZ
values:
name: Manish
last: Raut
And in my class file i am able to get the values of "company" from yml file using #Value("${company}")
#Component
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "details")
public class abcd() {
#Value("${company}")
String company;
#Value("${values}")
Map<String, String> values =new HashMap<String, String>();
...............................
}
i am not able to get those values in Mao which i created in this class, but i am getting values for "Company".
Help me with this?
Im the past, I added getter/setter for MAP type and it was work.
Did you try it? (getter/setter for 'values')
I'm not really sure what marshalling framework Spring uses behind the scenes and how it configures it (you could probably find out with some debugging and maybe make it work for your case), but you could always add an extra layer to your application and configure your own.
For instance you could use Jackson with yaml dataformat - https://dzone.com/articles/read-yaml-in-java-with-jackson.

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.

Categories

Resources