Search code examples
javahashmapguavasnakeyaml

SnakeYAML load into Guava MultiMap


I am trying to load a Yaml file into a MultiMap using SnakeYAML, but I keep encountering the following exception: java.base/java.util.LinkedHashMap cannot be cast to com.google.common.collect.Multimap. Is there any way to efficiently and effectively load a SnakeYAML object into a Guava MultiMap? I am aware that you can do this with regular HashMaps, but my target Yaml has duplicate keys, so it requires the use of MultiMaps. Thanks for the help in advance. My code for populating the MultiMap using SnakeYAML is as follows:

//Read the config file
InputStream configIn;
try {
    configIn = FileUtil.loadFileAsStream(configPath);

    //Load the config file into the YAML object
    pluginConf = LinkedHashMultimap.create((Multimap<String, Object>) configData.load(configIn));
} 
catch(FileNotFoundException e){
    // TODO Auto-generated catch block
    e.printStackTrace();
}

EDIT: Sample YAML that has duplicate keys

join:
  message: "JMessage1"
quit:
  message: "QMessage1"
join:
  message: "JMessage2"
quit:
  message: "QMessage2"
join:
  message: "JMessage3"
quit:
  message: "QMessage3"

Solution

  • I did manage to solve this problem, but I used regex with regular HashMaps instead of the Guava Multimaps like I originally intended. For example, here's a YAML file that has duplicated keys:

    join:
      message: "JMessage1"
    quit:
      message: "QMessage1"
    join:
      message: "JMessage2"
    quit:
      message: "QMessage2"
    

    To parse out all of the messages, all I did was simply add numbers to each of the parent keys that were being repeated (join becomes join1, quit becomes quit1, and so on). Since each key has a number in it, they can easily be reverted back to just saying join or quit with the following regex: str.replaceAll("[^a-zA-Z.]", "").toLowerCase() in a for loop that iterates through the HashMap. Since the HashMap entries now just read as join, quit, join, quit when put through the loop, their values can be easily grabbed using something like the following snippet:

    if(entry.equals("join")){
        //Do stuff
    }
    

    After that, the values can be added to something like an ArrayList or other collection. In my case, I used an ArrayList and assigned the properties of the YAML file to instance variables of an object. The following snippet from one of my classes in the project accomplishes this:

    //Iterate through the HashMap
    for(Entry<?, ?> configEntry : pluginConf.entrySet()){
        //Get a string from the selected key that contains only lower-case letters and periods
        String entryKey = configEntry.getKey().toString().replaceAll("[^a-zA-Z.]", "").toLowerCase();
    
        //Search for join messages
        if(entryKey.equals("messages.join")){
            //Get the key name of the current entry
            String joinKeyName = configEntry.getKey().toString();
    
            //Create a join message object
            JoinMessage newJoinMessage = new JoinMessage(,
                configData.getString(joinKeyName + ".message")
            );
    
            //Add the join message object to the join message ArrayList
            joinMessages.add(newJoinMessage);
    
            //Add to the join message quantity
            joinMsgQuantity ++;
        }
    }
    

    Not really using HashMaps that allow duplicates like I originally asked for, but this "hack" worked flawlessly for me. Hope this helps anyone else looking to do something like this.