[Yaml] Escape strings that contain special characters
Fixes encoding errors or ambiguity in some text editors Signed-off-by: Irmo van den Berge <irmo.vandenberge@ziggo.nl>
This commit is contained in:
1 parent
3db69e3225
commit
2e69527175
2 files changed
+87
-5
No files matched your search
@@ -20,6 +20,7 @@ import org.yaml.snakeyaml.events.StreamEndEvent;
|
||||
import org.yaml.snakeyaml.events.StreamStartEvent;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.representer.BaseRepresenter;
|
||||
import org.yaml.snakeyaml.representer.Represent;
|
||||
import org.yaml.snakeyaml.resolver.Resolver;
|
||||
import org.yaml.snakeyaml.serializer.Serializer;
|
||||
|
||||
@@ -54,6 +55,7 @@ public class YamlSerializer {
|
||||
/**
|
||||
* Creates a new YamlSerializer
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public YamlSerializer() {
|
||||
headerPrefixes = new String[] { "#> ", "# " };
|
||||
dumperOptions = new DumperOptions();
|
||||
@@ -61,11 +63,40 @@ public class YamlSerializer {
|
||||
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
indentStr = StringUtil.getFilledString(" ", dumperOptions.getIndent());
|
||||
resolver = new Resolver();
|
||||
representer = new YamlRepresenter();
|
||||
representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle());
|
||||
representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle());
|
||||
representer.getPropertyUtils().setAllowReadOnlyProperties(dumperOptions.isAllowReadOnlyProperties());
|
||||
representer.setTimeZone(dumperOptions.getTimeZone());
|
||||
representer = createRepresenter(dumperOptions);
|
||||
|
||||
// Alter the registered String representer, making it quote strings that contain []{}&$
|
||||
// See: stringMustBeQuoted(String)
|
||||
try {
|
||||
Field representersField = org.yaml.snakeyaml.representer.BaseRepresenter.class.getDeclaredField("representers");
|
||||
representersField.setAccessible(true);
|
||||
|
||||
// Create a RepresentString instance that always quotes
|
||||
final Represent quotedStringRepresent;
|
||||
{
|
||||
YamlRepresenter quotedRepresenter = createRepresenter(dumperOptions);
|
||||
quotedRepresenter.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
|
||||
Map<Class<?>, Represent> quotedRepresentersByType = (Map<Class<?>, Represent>) representersField.get(quotedRepresenter);
|
||||
quotedStringRepresent = quotedRepresentersByType.get(String.class);
|
||||
if (quotedStringRepresent == null) {
|
||||
throw new IllegalStateException("No String representer");
|
||||
}
|
||||
}
|
||||
|
||||
// Update the String representer, and switch dynamically between quoted and the default
|
||||
// depending on the input data value
|
||||
Map<Class<?>, Represent> representersByType = (Map<Class<?>, Represent>) representersField.get(representer);
|
||||
representersByType.computeIfPresent(String.class, (clsKey, defaultStringRepresent) -> data -> {
|
||||
if (stringMustBeQuoted((String) data)) {
|
||||
return quotedStringRepresent.representData(data);
|
||||
} else {
|
||||
return defaultStringRepresent.representData(data);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
Logging.LOGGER_CONFIG.log(Level.WARNING, "[Yaml] Failed to enable escaping of special characters", t);
|
||||
}
|
||||
|
||||
output = new StringBuilderWriter();
|
||||
resetMethod = new FastMethod<Void>();
|
||||
builder = output.getBuilder();
|
||||
@@ -204,6 +235,15 @@ public class YamlSerializer {
|
||||
}
|
||||
}
|
||||
|
||||
private static YamlRepresenter createRepresenter(DumperOptions dumperOptions) {
|
||||
YamlRepresenter representer = new YamlRepresenter();
|
||||
representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle());
|
||||
representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle());
|
||||
representer.getPropertyUtils().setAllowReadOnlyProperties(dumperOptions.isAllowReadOnlyProperties());
|
||||
representer.setTimeZone(dumperOptions.getTimeZone());
|
||||
return representer;
|
||||
}
|
||||
|
||||
private String build() {
|
||||
String str = this.output.toString();
|
||||
builder.setLength(0);
|
||||
@@ -486,6 +526,18 @@ public class YamlSerializer {
|
||||
"false", "False", "FALSE"
|
||||
));
|
||||
|
||||
// Whether the String must always be quoted.
|
||||
private static boolean stringMustBeQuoted(String str) {
|
||||
int len = str.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = str.charAt(i);
|
||||
if (c == '[' || c == ']' || c == '{' || c == '}' || c == '&' || c == '$') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks whether a String can be written as a String literal, without ' or "
|
||||
// This is a fast check for the most common case of only a-zA-Z key names
|
||||
private boolean canWriteStringLiteral(String str, boolean isKey) {
|
||||
|
||||
@@ -31,6 +31,36 @@ import com.bergerkiller.mountiplex.MountiplexUtil;
|
||||
|
||||
public class YamlTest {
|
||||
|
||||
@Test
|
||||
public void testYamlSpecialStringEscaping() {
|
||||
YamlNode root = new YamlNode();
|
||||
root.set("test1", " Word");
|
||||
root.set("test2", "Word ");
|
||||
root.set("test3", "Word:");
|
||||
root.set("test4", "Word[]");
|
||||
root.set("test5", "Word{}");
|
||||
root.set("test6", "Word:[]");
|
||||
root.set("test7", "Word:{}");
|
||||
root.set("test8", "'Quoted'");
|
||||
root.set("test9", "\"Quoted\"");
|
||||
root.set("test10", "$Hello World");
|
||||
root.set("test11", "&Hello World");
|
||||
root.set("test12", "");
|
||||
assertEquals("" +
|
||||
"test1: ' Word'\n" +
|
||||
"test2: 'Word '\n" +
|
||||
"test3: 'Word:'\n" +
|
||||
"test4: 'Word[]'\n" +
|
||||
"test5: 'Word{}'\n" +
|
||||
"test6: 'Word:[]'\n" +
|
||||
"test7: 'Word:{}'\n" +
|
||||
"test8: '''Quoted'''\n" +
|
||||
"test9: '\"Quoted\"'\n" +
|
||||
"test10: '$Hello World'\n" +
|
||||
"test11: '&Hello World'\n" +
|
||||
"test12: ''\n", root.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testYamlKeyAPIs() {
|
||||
YamlNode root = new YamlNode();
|
||||
|
||||
Reference in new issue
Block a user