Emanuele Vannacci
Emanuele Vannacci

Reputation: 321

yaml YAMLException: How to configure using SnakeYaml?

I'm tryng to use SnakeYaml library to configure my project using a YAML file. I have already read this example: Here

I follow that structure and I have these files:

config/statisticsConfig.yml:

statisticsTopologyParams:
   tickTupleFrequency: 60

   hourlyStatistics: 
     windowLength: 3600
     emitFrequency: 60 

   dailyStatistics: 
     windowLength: 86400
     emitFrequency: 3600 

Configuration.java:

 public class Configuration {
     Map<String, ServiceConfig> statisticsTopologyParams;

     public Configuration() {
     }

     @Override
     public String toString() {
         return "YamlConfig{" +
                 "statistics=" + statisticsTopologyParams +
                 '}';
     }

     public Map<String, ServiceConfig> getStatisticsTopologyParams() {
         return statisticsTopologyParams;
     }

     public void setStatisticsTopologyParams(Map<String,        ServiceConfig> statisticsTopologyParams) {
         this.statisticsTopologyParams = statisticsTopologyParams;
     }

}

ServiceConfig.java:

public class ServiceConfig {

    private Integer tickTupleFrequency;
    private Map<String, Integer> hourlyStatistics;
    private Map<String, Integer> dailyStatistics;

    public ServiceConfig() {
    }

    public Integer getTickTupleFrequency() {
        return tickTupleFrequency;
    }

    public void setTickTupleFrequency(Integer tickTupleFrequency) {
        this.tickTupleFrequency = tickTupleFrequency;
    }

    public Map<String, Integer> getHourlyStatistics() {
        return hourlyStatistics;
    }

    public void setHourlyStatistics(Map<String, Integer> hourlyStatistics) {
        this.hourlyStatistics = hourlyStatistics;
    }

    public Map<String, Integer> getDailyStatistics() {
        return dailyStatistics;
    }

    public void setDailyStatistics(Map<String, Integer> dailyStatistics) {
        this.dailyStatistics = dailyStatistics;
    }
}

YamlConfigRunner.java:

public class YamlConfigRunner {


    public Configuration getConfiguration(String filePath) throws IOException {


        Constructor constructor = new Constructor(Configuration.class);
        Yaml yaml = new Yaml(constructor);

        try (InputStream in = Files.newInputStream(Paths.get(filePath))) {
            Configuration config = yaml.loadAs(in, Configuration.class);
            System.out.println(config.toString());
            return config;
        }
    }

}

However I obtain the exception:

null; Can't construct a java object for tag:yaml.org,2002:org.uniroma2.sdcc.Utils.Configuration; exception=Cannot create property=statisticsTopologyParams for JavaBean=YamlConfig{statistics=null}; No single argument constructor found for class org.uniroma2.sdcc.Utils.ServiceConfig;  in 'reader', line 1, column 1:
statisticsTopologyParams:


 Caused by: org.yaml.snakeyaml.error.YAMLException: No single argument constructor found for class org.uniroma2.sdcc.Utils.ServiceConfig

Upvotes: 3

Views: 8127

Answers (1)

flyx
flyx

Reputation: 39688

The problem is that you are telling SnakeYaml to convert a number (60) into a ServiceConfig object. Since ServiceConfig does not have a constructor which takes exactly one argument, SnakeYaml does not know how to do that.

To go into more detail, this is the failing line:

  tickTupleFrequency: 60

tickTupleFrequency here is the key of a Map<String, ServiceConfig>. It is no problem to load it as String, so SnakeYaml does that. Now, there is a value 60. This must be converted into a ServiceConfig object to fit into the Map. How is SnakeYaml supposed to do that? It doesn't know, and therefore, yield the error.

You seem to have simply forgotten to add the Map level to your YAML, since both tickTupleFrequency and the following keys are all fields of a ServiceConfig object. So maybe this is what you actually want to have:

statisticsTopologyParams:
  My fancy ServiceConfig:
    tickTupleFrequency: 60

    hourlyStatistics: 
      windowLength: 3600
      emitFrequency: 60 

    dailyStatistics: 
      windowLength: 86400
      emitFrequency: 3600

Now, My fancy ServiceConfig will be the key in the Map<String, ServiceConfig> and the containing map will be loaded as ServiceConfig object.

Upvotes: 3

Related Questions