Using yaml in my Spring-boot application (with snakeyaml dependency 1.16) I am attempting to create a #ConfigurationProperties based off of my application.yml file. I want to create a data structure like the json below which is a Map with String Keys and Array values.
mapName: {
"key1": ["elem0","elem1"],
"key2": ["hello","world"]
}
Attempting to create a Spring configuration class as follows
#Component
#ConfigurationProperties(prefix = "channel-broker")
#EnableConfigurationProperties
public class BrokerConfiguration {
private Map<String, Set<String>> broker = new HashMap<>();
public Map<String, Set<String>> getBroker() {
return broker;
}
}
I have tried the following for my yaml
channel-broker:
broker: {message-delivery: ['all'], facebook: ['client1']}
Attempt two
channel-broker:
message-delivery: ['all']
facebook: ['client1']
Attempt three
channel-broker:
message-delivery:
- ['all']
facebook:
- ['client1']
I have also tried initializing the HashMap in the #ConfigurationProperties class as such ... new HashMap<String, Set<String>> this didn't work either
All attempts result in this error which makes me believe its an error when converting to the object not that there is anything wrong with the yaml syntax.
Caused by: org.springframework.beans.InvalidPropertyException: Invalid
property 'brokerTest[message-delivery][0]' of bean class
[my.classpackage.clasname]:
Property referenced in indexed property path
'brokerTest[message-delivery][0]' is neither an array nor a List nor a
Map; returned value was [all]
Is it possible to create such an object? How would I accomplish this
-UPDATE-
If I change the Set to an ArrayList (or List interface) this works but that isn't what I'm looking for. changed to this
private Map<String, ArrayList<String>> brokerTest = new HashMap<>();
but need this doesn't work with Set interface either:
private Map<String, HashSet<String>> brokerTest = new HashMap<>();
This issue was being caused by the format of the yaml file. The following structure allowed me to build my graph like data structure out of yaml
channel-broker:
broker:
message-delivery:
all
facebook:
client1,client2
The Set doesn't want anything extra surrounding the key. Note if your Set will contain multiple values you can add a comma to separate them. Just like Json the last element will not have a comma after.
What you are looking for is this :
channel-broker: {broker: {message-delivery:['all', ...], facebook:['client1', ...]}}
see Complete idiot's introduction to yaml
If you use [] then it's an array so arraylist works, for hashset/hashmap you need to use {} brackets.
channel-broker: {
broker: {
message-delivery:{'all', '123'},
facebook:{'client1', 'cleant2'}
}
}
will work for hashset.
(hashmap example)
Related
I am still quite new to programming and I really hope anyone can help me.
I would like to create a Map from a Yaml file. The problem is, I would like to make it optional which parameters are configured, so I do not want to create a class, in which all possible parameters are created as variables. I have used snakeyaml before, however as far as I know this is not an option in snakeyaml.
An example for my yaml file would look like this:
description: linter for microservices
meta:
pciScope: false
image:
name: helmcube
tag: 2.5.4
service:
type: 45
deployment:
replicaCount: 2
Do any of you have an idea how this could be realised? I have already searched for hours and could not find anything concerning that issue.
I hope anyone can help
Regards
Marie
Would getting a Properties instance be sufficient for you? Instead of creating your own Class with defined variables.
If you use Spring,
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource)
throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
https://www.baeldung.com/spring-yaml-propertysource
What's the best way to load a yaml file to Map(not an environment configuration file) in spring boot web project?
--- Edited ---
You now have a Properties, which you can in turn get a Hashmap out of:
Using external Lib Guava,
com.google.common.collect.Maps.fromProperties(Properties)
Or the Java Way:
Map properties = new Properties();
HashMap<String, String> map = new HashMap<String, String>(properties);
This stackoverflow shows you how to convert Properties into HashMap:
Converting java.util.Properties to HashMap<String,String>
Once you have a hasmap of configurations(Strings), you could compare against another hashamap of configurations (Strings)
If this does not help you, kindly update your question with clear incentives/example of what you are trying to achieve.
We have one application where we use configuration files and they have fields as arrays and normal variables:
metadata {
array=["val1", "val2"]
singleValue=2.0
}
Now, I know how to extract these above values like
config.getStringList("metadata.array").asScala.toArray
and config.getString("metadata.singleValue)
But, is there any way I can define maps here so that I can find value wrt desired key from that map.
This config is an object of
public interface Config extends com.typesafe.config.ConfigMergeable
You can use config.getConfig("metadata") to obtain a (sub)config object.
Converting the (sub)config to a map is something you'll have to do yourself. I would use config.entrySet() to obtain the entries as key-values, and load it into a map that way.
I haven't tried compiling/testing this code, but something like this should work:
Map<String,Object> metadata = new HashMap<>();
for (Map.Entry<String,ConfigValue> entry : config.entrySet()) {
metadata.put(entry.getKey(), entry.getValue().unwrapped());
}
I am currently programming a bukkit plugin that stores a bunch of information about the player in a YAML configuration file. Now I want the plugin to read the YAML file when the server starts up and then add on the that information. I have my loader, but I cant use it because my plugin uses a custom map. Here is the code for the map:
Map<Integer, Map<String, Object>>
And here is the code to get the information from the file:
info = (Map<Integer, Map<String, Object>>) ticket.getConfigurationSection("tickets");
But when I try to run the plugin with that line of code i get this error:
Caused by: java.lang.ClassCastException: org.bukkit.configuration.MemorySection cannot be cast to java.util.Map
Full code is posted here: http://pastebin.com/Xgu8hwM0
The solution to this is not using a custom map. You already get a MemorySection from your configuration.
Work with that. Instead of casting you should use the method: getValues(boolean) which returns a Map<String, Object> containing all the relevant information and is specified by the Interface ConfigurationSection.
ticket.getConfigurationSection("tickets").getValues();
See also the relevant excerpt at bukkit's Configuration API Reference:
The getValues method will return the values in the
ConfigurationSection as a map, it takes a boolean which controls if
the nested maps will be returned in the map.
Yes I solved this. I HAD to use the Map<String, Object> but it worked because the way I had it(Map<Integer, Map<String, Object>>) that is the second part!
How does one populate a map of values using the #Values annotation, without defining anything in applicationContext.xml or any other XML file.
I am using spring boot, which doesn't have any XML files, and nor do I want any XML files, so please don't tell me to declare any property reader beans in XML etc.
Also, this is a properties injection question - please don't suggest using a database to store the data - that's not an answer, and not possible for my situation anyway.
Also, I can't use YAML either (due to deployment/operational requirements).
I have tried declaring this injection:
#Value("${myprop}")
Map<Integer, String> map;
And this one
#Value("${myprop.*}")
Map<Integer, String> map;
with these entries application.properties:
myprop.1=One
myprop.2=Two
myprop.3=Three
and then tried
myprop[1]=One
myprop[2]=Two
myprop[3]=Three
But no good - just explodes with
Could not autowire field: ... Could not resolve placeholder 'myprop'
I have found a work-around with an injected String[] specified as key1:value1,key2:value2,... that I then parse in code, but I'd prefer to not do that because a) it's more code, and b) the list is going to be quite long, and all pairs on one line is going to be hard to read and maintain.
Is there a way to automatically build a map from several properties?
I don't care what the property names are, what the field type or the annotation is; I'm just trying to inject one key/value pair per property.
Not sure if this applies to your scenario entirely (you have there a Map<Integer, String> but in the end you say you just need a key-value pair in a Map), but maybe it could give you some more ideas:
assuming a #Configuration class where the .properties file is loaded as a java.util.Properties object:
#Configuration
public class Config {
#Bean(name = "mapper")
public PropertiesFactoryBean mapper() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("META-INF/spring/application.properties"));
return bean;
}
#Bean
public MyBean myBean() {
return new MyBean();
}
}
the MyBean class where those Properties are being used, injected using SPeL:
public class MyBean {
#Value("#{mapper}")
private Map props;
public Map getProps() {
return props;
}
}
So, in the end you don't use xml (of course), you need to use a PropertiesFactoryBean to load the .properties file and, using #Value, Spring will inject the Properties into a Map. The extra code (compared to, probably, #PropertySource) is the PropertiesFactoryBean and you don't need to parse the values in your code manually (compared to your workaround that injects a String[]).
Hope this helps.
How about defining a bean in your Java config for this?
#Bean
public Map<Integer, String> myProps(Properties properties) {
Map<Integer, String> map = new HashMap<>();
// implement logic to populate map from properties
return map;
}
And in your class:
#Autowirded
Map<Integer, String> map;
Hello for people serach a simply solution to this problem. 😄
put the sequent line to your application.properties:
myprop = "{1:'One',2:'Two',3:'Three'}"
then in your Spring application put the line:
#Value("#{${myprop}}")
Map<Integer, String> map;
I have a class which has a Map as its member variable. Something like this -
public Clas Engine{
private Map<String,List<String>> filesByKey;
public void setFilesByKey(Map<String,List<String>> map) {
this.filesByKey = map;
}
public Map<String,List<String>> getFilesByKey() {
return filesByKey;
}
}
User can specify any number of keys in the map and its not predefined concept. They can basically group any number of files into one key and provider the map Value.
I was using PropertyOverrideConfigurer and in the properties file, I was trying to do something like this -
engine.filesByKey[key1][0]=file1
engine.filesByKey[key1][1]=file2
engine.filesByKey[key2][0]=anotherfile1
engine.filesByKey[key2][1]=anotherfile2
Now this is not working because the the List value corresponding to key1 or key2 is null to being with. So Spring Bean creation fails with the message that it can not set value to a property which is NULL.
What is the best way to handle this situation?
You should be able to use the LazyMap & LazyList from commons collections to achieve this.
Try to initialize your filesByKey variable with DefaultedMap from commons collections. It can return default value instead of null if map doesn't contain required key.