I'm reading and writing YML content into files. Some files are like that:
&id001
playername: SomeName
lang: fr_FR
minerate-full-mined: *id001
The issue is that the minerate-full-mined
key refer to the first line of file, so it make an infinite loop.
My code:
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(new Representer(options) {
{
this.representers.put(Configuration.class, (data) -> represent(((Configuration) data).self));
}
}, options);
protected Map<String, Object> self;
protected Configuration defaults;
protected File file;
public Configuration() {
this(null);
}
public Configuration(Configuration defaults) {
this(null, new LinkedHashMap<String, Object>(), defaults);
}
public Configuration(File file, Map<?, ?> map, Configuration defaults) {
this.file = file;
this.self = new LinkedHashMap<String, Object>();
this.defaults = defaults;
for (Map.Entry<?, ?> entry : map.entrySet()) {
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
if (entry.getValue() instanceof Map) {
this.self.put(key, new Configuration(file, (Map<?, ?>) entry.getValue(), defaults == null ? null : defaults.getSection(key)));
} else {
this.self.put(key, entry.getValue());
}
}
}
The infinite loop is made at the line this.self.put(key, new Configuration[...]);
.
I tried to print entry.getValue()
, and it's printed as {playername: SomeName, lang: fr_FR, minerate-full-mined=(This Map)}
. So it can be detected, but I don't know that we can put a map on itself.
How can I prevent snakeyaml to take this in count ?
I made a trick to skip it and prevent this type of issue, like that:
Configuration(File file, Map<?, ?> map, Configuration defaults) {
this.file = file;
this.self = new LinkedHashMap<String, Object>();
this.defaults = defaults;
for (Map.Entry<?, ?> entry : map.entrySet()) {
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
if (entry.getValue() instanceof Map) {
Configuration nextDef = defaults == null ? null : defaults.getSection(key);
String checkingStr = entry.getValue().toString();
if (checkingStr.contains(key + "=(this Map)") || checkingStr.contains(key + "=(this Collection)"))
continue;
this.self.put(key, new Configuration(file, (Map<?, ?>) entry.getValue(), nextDef == this ? null : nextDef)); // prevent infinite loop
} else {
this.self.put(key, entry.getValue());
}
}
}
With checkingStr.contains(key + "=(this Map)") || checkingStr.contains(key + "=(this Collection)")
I can skip all map to be added into themself.