Carleslc / Simple-YAML

This Java API provides an easy-to-use way to store data and provide configurations using the YAML format.
https://carleslc.me/Simple-YAML
GNU General Public License v3.0
127 stars 38 forks source link

String List placement inside yaml #78

Open ItsRainingHP opened 1 year ago

ItsRainingHP commented 1 year ago

Using a List of characters results in a YAML does not make sense and is not working.

I will edit and place code if you need me to though its very basic.

Code:

image

Mixed Results:

Worked for some reason - image Did not work at all - image

Carleslc commented 1 year ago

I cannot reproduce it. This is my working code:

YamlFile yamlFile = new YamlFile("issue78.yml");

yamlFile.createOrLoadWithComments();

// Comment format

String lineDecorator = StringUtils.padding(30, '-'); // "-".repeat(30) with Java 11+

YamlCommentFormatter commentFormatter = yamlFile.options().commentFormatter();

commentFormatter.blockFormatter()
        .prefix("#" + lineDecorator + "#\n# ", YamlCommentFormatterConfiguration.DEFAULT_COMMENT_PREFIX)
        .suffix("\n#" + lineDecorator + "#");

// WebLore

ArrayList<String> webLore = new ArrayList<>();

webLore.add("Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful " +
        "perks and commands to use. The path to the top has many different options and requirements.");
webLore.add("One could be a crafter, building vast kingdoms and controlling many different animals. " +
        "Alternatively, one could decide to pursue the path of darkness. " +
        "Murdering players and bosses alike as they continue the path of becoming the strongest.");
webLore.add("However, to be considered the pinnacle of strength in mind and body alike, " +
        "one must walk every path; obtaining glory in both crafting and combat and " +
        "claiming a seat among the Overlords.");

yamlFile.setComment("WebLore", "WebLore\nLore in the Web GUI");
yamlFile.set("WebLore", webLore);

// Save

yamlFile.save();

// Output

System.out.println("WebLore");
System.out.println(String.join("\n", yamlFile.getStringList("WebLore")));

I've included an example for your comment format configuration. I've excluded imports and exception handling.

Output

WebLore
Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful perks and commands to use. The path to the top has many different options and requirements.
One could be a crafter, building vast kingdoms and controlling many different animals. Alternatively, one could decide to pursue the path of darkness. Murdering players and bosses alike as they continue the path of becoming the strongest.
However, to be considered the pinnacle of strength in mind and body alike, one must walk every path; obtaining glory in both crafting and combat and claiming a seat among the Overlords.

issue78.yml

#------------------------------#
# WebLore
# Lore in the Web GUI
#------------------------------#
WebLore:
  - Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful
    perks and commands to use. The path to the top has many different options and
    requirements.
  - One could be a crafter, building vast kingdoms and controlling many different
    animals. Alternatively, one could decide to pursue the path of darkness. Murdering
    players and bosses alike as they continue the path of becoming the strongest.
  - However, to be considered the pinnacle of strength in mind and body alike, one
    must walk every path; obtaining glory in both crafting and combat and claiming
    a seat among the Overlords.

In addition, you can also try to disable splitting lines, maybe that's causing problems with your code:

// ...
// yamlFile.createOrLoadWithComments();

// Disable split lines

DumperOptions yamlOptions = ((SimpleYamlImplementation) yamlFile.getImplementation()).getDumperOptions();

yamlOptions.setSplitLines(false);

// ...

issue78.yml setSplitLines(false)

#------------------------------#
# WebLore
# Lore in the Web GUI
#------------------------------#
WebLore:
  - Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful perks and commands to use. The path to the top has many different options and requirements.
  - One could be a crafter, building vast kingdoms and controlling many different animals. Alternatively, one could decide to pursue the path of darkness. Murdering players and bosses alike as they continue the path of becoming the strongest.
  - However, to be considered the pinnacle of strength in mind and body alike, one must walk every path; obtaining glory in both crafting and combat and claiming a seat among the Overlords.
Carleslc commented 1 year ago

Also, why do you want to use a list of strings for that? That seems a block of text description, for that you can use a string literal:

// WebLore Literal

String webLoreString = "Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful " +
        "perks and commands to use. The path to the top has many different options and requirements.\n" +
        "One could be a crafter, building vast kingdoms and controlling many different animals. " +
        "Alternatively, one could decide to pursue the path of darkness. " +
        "Murdering players and bosses alike as they continue the path of becoming the strongest.\n" +
        "However, to be considered the pinnacle of strength in mind and body alike, " +
        "one must walk every path; obtaining glory in both crafting and combat and " +
        "claiming a seat among the Overlords.";

yamlFile.path("WebLoreLiteral")
        .comment("WebLore\nLore in the Web GUI")
        .set(webLoreString, QuoteStyle.LITERAL);

// Save

yamlFile.save();

// Output

System.out.println("WebLoreLiteral");
System.out.println(yamlFile.getString("WebLoreLiteral"));

Instead of string concatenation, if you use Java 15+ you can also use text blocks to improve the code readability of large string blocks.

Note that using yamlFile.path is an alternative to yamlFile.set and yamlFile.setComment to avoid writing the same key multiple times.

Output

WebLoreLiteral
Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful perks and commands to use. The path to the top has many different options and requirements.
One could be a crafter, building vast kingdoms and controlling many different animals. Alternatively, one could decide to pursue the path of darkness. Murdering players and bosses alike as they continue the path of becoming the strongest.
However, to be considered the pinnacle of strength in mind and body alike, one must walk every path; obtaining glory in both crafting and combat and claiming a seat among the Overlords.

issue78.yml (Literal String)

#--------------------#
# WebLore
# Lore in the Web GUI
#--------------------#
WebLoreLiteral: |-
  Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful perks and commands to use. The path to the top has many different options and requirements.
  One could be a crafter, building vast kingdoms and controlling many different animals. Alternatively, one could decide to pursue the path of darkness. Murdering players and bosses alike as they continue the path of becoming the strongest.
  However, to be considered the pinnacle of strength in mind and body alike, one must walk every path; obtaining glory in both crafting and combat and claiming a seat among the Overlords.
ItsRainingHP commented 1 year ago

I will try splitting the lines. the webLore list prints out just fine for me, its the placement when writing to default that is giving mixed results.

I need the values to be a List of Strings. I do not want to use a literal because I do not want to have to split the String after reading the value in. Additionally, users easily understand how to create a list of Strings.

ItsRainingHP commented 1 year ago

This fixed the issue thank you!

DumperOptions yamlOptions = ((SimpleYamlImplementation) yamlFile.getImplementation()).getDumperOptions();

yamlOptions.setSplitLines(false);
ItsRainingHP commented 1 year ago

I was wrong. I still have an issue where its using a |- for the String list. This results in failing to read the list in. Is it possible for force the the yaml file to only use the array or list formatting? So that it only uses the following:

Option:
  - item1
  - item2
  - item3

or

Option: [item1, item2, item3]
Carleslc commented 1 year ago

There is no configuration option for that. You may do it with a custom representer (see the comment below), but first, we would need to know where the problem is, if it's something with your code, with Simple-YAML or with the snakeyaml implementation. For that, you need to provide a minimal code to reproduce your issue.

As I said, I can't reproduce the issue, this code similar to the one provided before is working for me:

YamlFile yamlFile = new YamlFile("issue78.yml");

yamlFile.createOrLoadWithComments();

// Disable split lines

DumperOptions yamlOptions = ((SimpleYamlImplementation) yamlFile.getImplementation()).getDumperOptions();

yamlOptions.setSplitLines(false);

// Comment format

String lineDecorator = StringUtils.padding(30, '-'); // "-".repeat(30) with Java 11+

YamlCommentFormatter commentFormatter = yamlFile.options().commentFormatter();

commentFormatter.blockFormatter()
        .prefix("#" + lineDecorator + "#\n# ", YamlCommentFormatterConfiguration.DEFAULT_COMMENT_PREFIX)
        .suffix("\n#" + lineDecorator + "#");

// WebLore

ArrayList<String> webLore = new ArrayList<>();

webLore.add("Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful " +
        "perks and commands to use. The path to the top has many different options and requirements.");
webLore.add("One could be a crafter, building vast kingdoms and controlling many different animals. " +
        "Alternatively, one could decide to pursue the path of darkness. " +
        "Murdering players and bosses alike as they continue the path of becoming the strongest.");
webLore.add("However, to be considered the pinnacle of strength in mind and body alike, " +
        "one must walk every path; obtaining glory in both crafting and combat and " +
        "claiming a seat among the Overlords.");

yamlFile.setComment("WebLore", "WebLore\nLore in the Web GUI");
yamlFile.set("WebLore", webLore);

// Save

yamlFile.save();
yamlFile.load(); // unnecessary, but to reset the memory values and load them again from file to check if it's reading it properly

// Output

System.out.println("WebLore");
System.out.println(String.join("\n", yamlFile.getStringList("WebLore")));

issue78.yml

#------------------------------#
# WebLore
# Lore in the Web GUI
#------------------------------#
WebLore:
  - Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful perks and commands to use. The path to the top has many different options and requirements.
  - One could be a crafter, building vast kingdoms and controlling many different animals. Alternatively, one could decide to pursue the path of darkness. Murdering players and bosses alike as they continue the path of becoming the strongest.
  - However, to be considered the pinnacle of strength in mind and body alike, one must walk every path; obtaining glory in both crafting and combat and claiming a seat among the Overlords.

Try that code to see if it is working for you.

Also, if you change yamlFile.set("WebLore", webLore); to yamlFile.set("WebLore", webLore, QuoteStyle.LITERAL); then you would get a literal list, with each list element as a string literal:

#------------------------------#
# WebLore
# Lore in the Web GUI
#------------------------------#
WebLore:
  - |-
    Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful perks and commands to use. The path to the top has many different options and requirements.
  - |-
    One could be a crafter, building vast kingdoms and controlling many different animals. Alternatively, one could decide to pursue the path of darkness. Murdering players and bosses alike as they continue the path of becoming the strongest.
  - |-
    However, to be considered the pinnacle of strength in mind and body alike, one must walk every path; obtaining glory in both crafting and combat and claiming a seat among the Overlords.

This still works, it's a list. But if you're getting |- in the whole value then it's treating it as a string, not a list.

Carleslc commented 1 year ago

As a reference, if you would like to use the array flow style for lists, this is how you can do it with a custom representer:

First, create a CustomYamlConfiguration class that inherits SimpleYamlImplementation. Here you can add your custom configurations so that doesn't bloat your main code.

import org.simpleyaml.configuration.comments.format.YamlCommentFormatter;
import org.simpleyaml.configuration.comments.format.YamlCommentFormatterConfiguration;
import org.simpleyaml.configuration.file.YamlConfigurationOptions;
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
import org.simpleyaml.utils.StringUtils;

public class CustomYamlConfiguration extends SimpleYamlImplementation {

    public CustomYamlConfiguration() {
        // Set custom representer
        super(new CustomListRepresenter(new DumperOptions()));
    }

    @Override
    public void configure(YamlConfigurationOptions options) {
        // Apply default configurations first
        super.configure(options);

        // Comment format
        String lineDecorator = StringUtils.padding(30, '-'); // "-".repeat(30) with Java 11+

        YamlCommentFormatter commentFormatter = options.commentFormatter();

        commentFormatter.blockFormatter()
                .prefix("#" + lineDecorator + "#\n# ", YamlCommentFormatterConfiguration.DEFAULT_COMMENT_PREFIX)
                .suffix("\n#" + lineDecorator + "#");

        // Disable split lines
        DumperOptions yamlOptions = getDumperOptions();

        yamlOptions.setSplitLines(false);
    }
}

I've moved the comment formatting and disabling split lines to the configure method.

In the constructor of this configuration, you would pass a custom representer that inherits SnakeYamlRepresenter. In this case, I named that class CustomListRepresenter. This custom representer will force the flow style for lists:

import org.simpleyaml.configuration.implementation.snakeyaml.SnakeYamlRepresenter;
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
import org.simpleyaml.configuration.implementation.snakeyaml.lib.nodes.Node;
import org.simpleyaml.configuration.implementation.snakeyaml.lib.nodes.Tag;

import java.util.ArrayList;
import java.util.List;

public class CustomListRepresenter extends SnakeYamlRepresenter {

    public CustomListRepresenter(DumperOptions dumperOptions) {
        super(dumperOptions);
        this.addClassTag(ArrayList.class, Tag.SEQ);
        this.multiRepresenters.put(List.class, new RepresentCustomList());
    }

    protected class RepresentCustomList extends RepresentList {

        @Override
        public Node representData(Object data) {
            Tag classTag = CustomListRepresenter.this.getTag(data.getClass(), Tag.SEQ);
            DumperOptions.FlowStyle flowStyle = DumperOptions.FlowStyle.FLOW;

            return representSequence(classTag, (List<?>) data, flowStyle);
        }

    }
}

The default FlowStyle is BLOCK, you can see it in CustomListRepresenter.this.defaultFlowStyle variable. Here we're forcing the list objects flow style to FLOW instead of the default flow style. You can explicitly set it to BLOCK if you want, but I don't think that would make a difference in comparison to the default style without a custom representer.

Then, in the main code, you set your custom configuration using the setImplementation method after creating the YamlFile:

YamlFile yamlFile = new YamlFile("issue78.yml");

// Set custom configuration
yamlFile.setImplementation(new CustomYamlConfiguration());

// Load file
yamlFile.createOrLoadWithComments();

// WebLore
ArrayList<String> webLore = new ArrayList<>();

webLore.add("Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful " +
        "perks and commands to use. The path to the top has many different options and requirements.");
webLore.add("One could be a crafter, building vast kingdoms and controlling many different animals. " +
        "Alternatively, one could decide to pursue the path of darkness. " +
        "Murdering players and bosses alike as they continue the path of becoming the strongest.");
webLore.add("However, to be considered the pinnacle of strength in mind and body alike, " +
        "one must walk every path; obtaining glory in both crafting and combat and " +
        "claiming a seat among the Overlords.");

yamlFile.setComment("WebLore", "WebLore\nLore in the Web GUI");
yamlFile.set("WebLore", webLore);

// Save
yamlFile.save();

// Output
System.out.println("WebLore");
System.out.println(String.join("\n", yamlFile.getStringList("WebLore")));

This is the result:

issue78.yml (Flow style for lists)

#------------------------------#
# WebLore
# Lore in the Web GUI
#------------------------------#
WebLore: ['Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful perks and commands to use. The path to the top has many different options and requirements.', 'One could be a crafter, building vast kingdoms and controlling many different animals. Alternatively, one could decide to pursue the path of darkness. Murdering players and bosses alike as they continue the path of becoming the strongest.', 'However, to be considered the pinnacle of strength in mind and body alike, one must walk every path; obtaining glory in both crafting and combat and claiming a seat among the Overlords.']

Or, removing the disable split lines (default setSplitLines(true)):

#------------------------------#
# WebLore
# Lore in the Web GUI
#------------------------------#
WebLore: ['Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many
    useful perks and commands to use. The path to the top has many different options
    and requirements.', 'One could be a crafter, building vast kingdoms and controlling
    many different animals. Alternatively, one could decide to pursue the path of
    darkness. Murdering players and bosses alike as they continue the path of becoming
    the strongest.', 'However, to be considered the pinnacle of strength in mind and
    body alike, one must walk every path; obtaining glory in both crafting and combat
    and claiming a seat among the Overlords.']

And withDumperOptions.FlowStyle.BLOCK style:

#------------------------------#
# WebLore
# Lore in the Web GUI
#------------------------------#
WebLore:
  - Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful
    perks and commands to use. The path to the top has many different options and
    requirements.
  - One could be a crafter, building vast kingdoms and controlling many different
    animals. Alternatively, one could decide to pursue the path of darkness. Murdering
    players and bosses alike as they continue the path of becoming the strongest.
  - However, to be considered the pinnacle of strength in mind and body alike, one
    must walk every path; obtaining glory in both crafting and combat and claiming
    a seat among the Overlords.
ItsRainingHP commented 1 year ago

The only difference between your code and mine is that I am using ConfigurationSection#addDefault instead of YamlFile#addDefault. Could that be the issue? I will try using YamlFile#addDefault and post results once I am able.

Carleslc commented 1 year ago

The only difference between your code and mine is that I am using ConfigurationSection#addDefault instead of YamlFile#addDefault. Could that be the issue? I will try using YamlFile#addDefault and post results once I am able.

My code uses the set method, but it's also working when using either addDefault or ConfigurationSection's addDefault.

Also remember you can use the path method to set values and comments without managing sections, as I wrote in this comment in another issue.

ItsRainingHP commented 1 year ago

Well then I really do not know why its behaving the way it is. Seriously frustrating a common String List is failing.

Here is my code. Happy to improve or change with your suggestions:

 public YamlFile getConfig() {
        YamlFile yamlFile = new YamlFile("/config.yml");
        try {
            if (!yamlFile.exists()) {
                yamlFile.createNewFile();
                yamlFile.loadWithComments();
                yamlFile.setCommentFormat(YamlCommentFormat.PRETTY);
                DumperOptions yamlOptions = ((SimpleYamlImplementation) yamlFile.getImplementation()).getDumperOptions();
                yamlOptions.setSplitLines(false);
                ArrayList<String> webLore = new ArrayList<>();
                webLore.add("Recruits are just the beginning. Wide-eyed and hopeful, recruits gain many useful " +
                        "perks and commands to use. The path to the top has many different options and requirements.");
                webLore.add("One could be a crafter, building vast kingdoms and controlling many different animals. " +
                        "Alternatively, one could decide to pursue the path of darkness. " +
                        "Murdering players and bosses alike as they continue the path of becoming the strongest.");
                webLore.add("However, to be considered the pinnacle of strength in mind and body alike, " +
                        "one must walk every path; obtaining glory in both crafting and combat and " +
                        "claiming a seat among the Overlords");
                //Let me know if you need to see the declared variables here
                createDefaultSection(yamlFile, "rank1", "<#55FF55>Recruit", "Dirt", lore,
                        webLore, false, requiredRanks, path, "rank1",
                        "<#55FF55>Recruit", "<#55FF55>the Recruit", 1, 1, commands);
            } else {
                yamlFile.loadWithComments();
            }
            try {
                yamlFile.save();
            } catch (IOException e) {
                RDQ.getInstance().sendLoggerWarning(e.getMessage());
            }
        } catch (final Exception e) {
            RDQ.getInstance().sendLoggerWarning(e.getMessage());
        }
        return yamlFile;
    }

    private void createDefaultSection(YamlFile yamlFile, String rank, String title, String material,
                                          List<String> lore, List<String> webLore, boolean showRequirements,
                                          List<String> requiredRanks, List<String> path, String luckPermsGroup,
                                          String prefix, String suffix, int weight, int tier, List<String> commands) {
        ConfigurationSection rank1 = YamlHandler.createOrGetSection(yamlFile, rank);
        rank1.addDefault("Title", title);
        rank1.addDefault("Material", material);
        rank1.addDefault("Lore", lore);
        rank1.addDefault("WebLore", webLore);
        rank1.addDefault("ShowRequirementsInGame", showRequirements);
        rank1.addDefault("RequiredRanks", requiredRanks);
        rank1.addDefault("Path", path);
        rank1.addDefault("LuckPermsGroup", luckPermsGroup);
        rank1.addDefault("Prefix", prefix);
        rank1.addDefault("Suffix", suffix);
        rank1.addDefault("Weight", weight);
        rank1.addDefault("Tier", tier);
        rank1.addDefault("Commands", commands);
    }