I have a Class like this:
public class MyClass
{
private int id;
private Map<String, String> myMap;
public Map<String, String> getMyMap()
{
return myMap;
}
public void setMyMap(Map<String, String> myMap)
{
this.myMap = myMap;
}
}
I added new setter method(overloading) because i didn't want to do set HashMap directly, and that's what you see now :
public class MyClass
{
private int id;
private Map<String, String> myMap;
public Map<String, String> getMyMap()
{
return myMap;
}
public void setMyMap(Map<String, String> myMap)
{
this.myMap = myMap;
}
public void setMyMap(String key , String value)
{
setMyMap(new HashMap<>(){{put(key, value);}});
}
}
But because i used new HashMap<>(){{put(key, value);}} keyword every time i use this method , it create new Map and last items deleted .
So i have 2 question:
1-correct solution for set items by 2nd setter method
2-how i could use this setter method for multiple put's for this situations:
MyClass.setMyMap(new HashMap<>()
{{
put("title", title);
put("id", id);
}});
Thank you guys for your time .
It depends on what your class does. But in general, I would not expose a setter for a map field.
It makes sense to add a constructor with a map argument, then do something like this:
public class MyClass
{
private final int id;
private final Map<String, String> myMap;
public MyClass(int id, Map<String, String> myMap) {
this.id = id;
this.myMap = myMap;
}
public Map<String, String> getMyMap()
{
return myMap;
}
public void addPairs(Map<String, String> pairs)
{
myMap.putAll(pairs);
}
public void addPair(String key, String value)
{
myMap.put(key, value);
}
}
Of course, you can expose an additional constructor:
public MyClass(int id) {
this.id = id;
this.myMap = new HashMap<>();
}
Try some thing like this:
public void setMyMap(String key , String value) {
if(myMap == null)
myMap = new HashMap<String, String>();
myMap.put(key, value);
}
You've already declared class field myMap and you want to use it in setMyMap method.
Do null check. If the field is null then create a new map. Then use put method to store data in the map.
Related
I have a Spring Boot application with the following application.yml
Detail_1:
name: X,Y,Z
place: xplace,yplace,zplace
Detail_2:
name: X,Y,Z
place: xplaceanother,yplaceanother,zplaceanother
How can I obtain this map in java:
X {
detail1 :xplace
detail2 :xplaceanother
}
Y {
detail1:yplace,
detail2:yplaceanother
}
Z{
detail1:zplace,
detail2:zplaceanother
}
I have tried the following code :
#Value${detail1.name}
private String names;
#value${detail2.place}
List<Object> Names = Arrays.asList(getNames().split(","));
List<Object> places = Arrays.asList(getPlaces().split(","));
Then I tried to create a map of names and places corresponding to detail 1
similarly I fetched names and places for detail 2
But In this case i end up with 2 maps , one for detail 1 and one for detail 2.
I need to create a single map.
You need to use #ConfigurationProperties annotation
The following URLs provide good examples in both .properties and .yml format:
https://www.mkyong.com/spring-boot/spring-boot-configurationproperties-example/
https://www.baeldung.com/configuration-properties-in-spring-boot
Please update your config like below in application.yml
map:
detail1:
name:X,Y,Z
place:xplace,yplace,zplace
detail2:
name:X,Y,Z
place:xplaceanother,yplaceanother,zplaceanother
and then configure the property as below,
DetailConfig.java
#Component
#ConfigurationProperties(prefix="map")
public class DetailConfig {
private Map<String, Object> detail1;
private Map<String, Object> detail2;
public Map<String, Object> getDetail1() {
return detail1;
}
public void setDetail1(Map<String, Object> detail1) {
this.detail1 = detail1;
}
public Map<String, Object> getDetail2() {
return detail2;
}
public void setDetail2(Map<String, Object> detail2) {
this.detail2 = detail2;
}
}
You can use the following pojo for property;
public class Detail {
private List<String> name;
private List<String> place;
public Map<String, String> getNamePlaceMap() {
return IntStream.range(0, name.size()).boxed()
.collect(Collectors.toMap(i -> name.get(i), i -> place.get(i)));
}
// getters/setters
}
and use the following configuration to get properties into context;
#Configuration
public class Config {
#Bean
#ConfigurationProperties(prefix = "detail-1")
public Detail detailOne() {
return new Detail();
}
#Bean
#ConfigurationProperties(prefix = "detail-2")
public Detail detailTwo() {
return new Detail();
}
}
and autowire them and pass them to the logic where that map is created;
#Service
public class TestService {
#Autowired
private Detail detailOne;
#Autowired
private Detail detailTwo;
public void test() {
System.out.println(createSpecialMap(detailOne, detailTwo));
}
private static Map<String, Map<String, String>> createSpecialMap(Detail detailOne, Detail detailTwo) {
Map<String, Map<String, String>> resultMap = new HashMap<>();
detailOne.getNamePlaceMap().forEach((key, value) -> {
Map<String, String> subMap = resultMap.getOrDefault(key, new HashMap<>());
subMap.put("detail1", value);
resultMap.put(key, subMap);
});
detailTwo.getNamePlaceMap().forEach((key, value) -> {
Map<String, String> subMap = resultMap.getOrDefault(key, new HashMap<>());
subMap.put("detail2", value);
resultMap.put(key, subMap);
});
return resultMap;
}
}
results in;
{
X={detail1=xplace, detail2=xplaceanother},
Y={detail1=yplace, detail2=yplaceanother},
Z={detail1=zplace, detail2=zplaceanother}
}
Or better in readability, using a Letter class;
public class Letter {
private String name;
private String detail1;
private String detail2;
public Letter(String name, String detail1, String detail2) {
this.name = name;
this.detail1 = detail1;
this.detail2 = detail2;
}
// getters/setters
}
doing the following;
private static List<Letter> createList(Detail detailOne, Detail detailTwo) {
List<Letter> resultList = new ArrayList<>();
Map<String, String> detailOneMap = detailOne.getNamePlaceMap();
Map<String, String> detailTwoMap = detailTwo.getNamePlaceMap();
Set<String> keySet = new HashSet<>();
keySet.addAll(detailOneMap.keySet());
keySet.addAll(detailTwoMap.keySet());
return keySet.stream()
.map(key -> new Letter(key, detailOneMap.get(key), detailTwoMap.get(key)))
.collect(Collectors.toList());
}
will result in;
[
Letter{name='X', detail1='xplace', detail2='xplaceanother'},
Letter{name='Y', detail1='yplace', detail2='yplaceanother'},
Letter{name='Z', detail1='zplace', detail2='zplaceanother'}
]
which is a better result than a raw map of map...
I want to provide a POST servlet that takes the following JSON content:
{
"name": John
"age": 25,
"some": "more",
"params: "should",
"get": "mapped"
}
Two of those properties should be explicit mapped to defined parameters. All other parameters should go into a Map<String, String>.
Question: how can I let Spring map them directly into the map of the bean?
#RestController
public void MyServlet {
#PostMapping
public void post(#RequestBody PostBean bean) {
}
}
public class PostBean {
private String name;
private String age;
//all other json properties should go here
private Map<String, String> map;
}
public class PostBean {
private Map<String, String> map;
#JsonAnyGetter
public Map<String, String> getMap() {
return map;
}
#JsonAnySetter
public void setMap(String name, String value) {
if (this.map == null) map = new HashMap<>();
this.map.put(name, value);
}
}
I create Details class and that class create Map object. and set getters setters.
setdatavalues class I set values to the setters
Then I try to get that values in getdatavalues class. by calling getters methord.
problem is I cannot get values in that getters. display empty array.
In getdatavalues calss I create mymap object and assign getMyMap() method and display the values
public class Details{
private Map<String, String> myMap = new LinkedHashMap<String, String>();
public Details() {
super();
}
public Map<String, String> getMyMap() {
return myMap;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
}
public static void setdatavalues(){
LinkedHashMap<String, String> myMap=new LinkedHashMap<String, String>();
ArrayList<String> fields,values=new ArrayList<String>();
Details details= new Details();
|
|
fields=readNumbers();
values=readStrings();
for(int j=0;j<fields.size();j++)
{
myMap.put(fields.get(j),values.get(j));
}
details.setMyMap(myMap);
}
}
public static void getdatavalues(){
Details details= new Details();
//System.out.println(details.getMyMap().values());
Map<String,String> mymap = details.getMyMap();
System.out.println(mymap.values());
}
output
details that is set values is setdatavalues is thrown away and new empty details is used in getdatavalues. You must pass the Details object that is set data to where data in Details object is printed to print the data set.
I have a class which looks like this:
#JsonFormat(shape=JsonFormat.Shape.OBJECT)
public class MyMap implements Map<String, String>
{
protected Map<String, String> myMap = new HashMap<String, String>();
protected String myProperty = "my property";
public String getMyProperty()
{
return myProperty;
}
public void setMyProperty(String myProperty)
{
this.myProperty = myProperty;
}
//
// java.util.Map mathods implementations
// ...
}
And a main method with this code:
MyMap map = new MyMap();
map.put("str1", "str2");
ObjectMapper mapper = new ObjectMapper();
mapper.getDeserializationConfig().withAnnotationIntrospector(new JacksonAnnotationIntrospector());
mapper.getSerializationConfig().withAnnotationIntrospector(new JacksonAnnotationIntrospector());
System.out.println(mapper.writeValueAsString(map));
When executing this code I'm getting the following output: {"str1":"str2"}
My question is why the internal property "myProperty" is not serialized with the map?
What should be done to serialize internal properties?
Most probably you will end up with implementing your own serializer which will handle your custom Map type. Please refer to this question for more information.
If you choose to replace inheritance with composition, that is to make your class to include a map field not to extend a map, then it is pretty easy to solve this using the #JsonAnyGetter annotation.
Here is an example:
public class JacksonMap {
public static class Bean {
private final String field;
private final Map<String, Object> map;
public Bean(String field, Map<String, Object> map) {
this.field = field;
this.map = map;
}
public String getField() {
return field;
}
#JsonAnyGetter
public Map<String, Object> getMap() {
return map;
}
}
public static void main(String[] args) throws JsonProcessingException {
Bean map = new Bean("value1", Collections.<String, Object>singletonMap("key1", "value2"));
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(map));
}
}
Output:
{"field":"value1","key1":"value2"}
I have an XML source from which I unmarshall Objects with JAXB.
The XML source:
<album>
<name>something</name>
<id>003030</id>
<artist>someone</artist>
...
</album>
The java source is like (with the required getter/setters as well):
#XmlRootElement(name="album")
class Album {
String name;
Long id;
String artist;
...
}
So far so good. Now I get some image urls in different sizes within album list:
...
<image size="small">http://.../small.jpg</image>
<image size="medium">http://.../medium.jpg</image>
<image size="large">http://.../large.jpg</image>
...
I want to map it to a java Map something like this:
Map<String,String> imageUrls;
Where the map's key would be the size attribute and the map's value would be the element value.
If it's possible, how should I annotate this variable?
helper class Pair
#XmlAccessorType(XmlAccessType.FIELD)
public class Pair {
#XmlAttribute
private String key;
#XmlValue
private String value;
public Pair() {
}
public Pair(String key, String value) {
this.key = key;
this.value = value;
}
//... getters, setters
}
List of pairs
#XmlAccessorType(XmlAccessType.FIELD)
public class PairList
{
private List<Pair> values = new ArrayList<Pair>();
public PairList() {
}
//...
}
adaptor
public class MapAdaptor extends XmlAdapter<PairList, Map<String, String>>
{
#Override
public Map<String, String> unmarshal(PairList list) throws Exception
{
Map<String, String> retVal = new HashMap<String, String>();
for (Pair keyValue : list.getValues())
{
retVal.put(keyValue.getKey(), keyValue.getValue());
}
return retVal;
}
#Override
public PairList marshal(Map<String, String> map) throws Exception
{
PairList retVal = new PairList();
for (String key : map.keySet())
{
retVal.getValues().add(new Pair(key, map.get(key)));
}
return retVal;
}
}
usage in your entity
#XmlJavaTypeAdapter(value = MapAdaptor.class)
private Map<String, String> imageUrls = new HashMap<String, String>();
PS
You can do it without class PairList using Pair[] instead of PairList
adaptor
public class MapAdaptor extends XmlAdapter<Pair[], Map<String, String>>
{
#Override
public Map<String, String> unmarshal(Pair[] list) throws Exception
{
Map<String, String> retVal = new HashMap<String, String>();
for (Pair keyValue : Arrays.asList(list))
{
retVal.put(keyValue.getKey(), keyValue.getValue());
}
return retVal;
}
#Override
public Pair[] marshal(Map<String, String> map) throws Exception
{
List<Pair> retVal = new ArrayList<Pair>();
for (String key : map.keySet())
{
retVal.add(new Pair(key, map.get(key)));
}
return retVal.toArray(new Pair[]{});
}
}
but in this case you can't control name of every pair. It will be item and you can't change it
<item key="key2">valu2</item>
<item key="key1">valu1</item>
PS2
If you will try use List<Pair> instead of PairList, you will get Exception
ERROR: java.util.List haven't no-arg constructor