Sping-Boot Config: How to keep whitespace in yaml key being used to populate Map<String, String> - spring-boot

Let's say we have a configuration properties class:
#ConfigurationProperties(prefix = "whitespace.test")
public class WhitespaceTestConfig {
private Map<String, String> configs;
public Map<String, String> getConfigs() {
return configs;
}
public void setConfigs(Map<String, String> configs) {
this.configs = configs;
}
}
and we try to configure it with a key with space included in it:
whitespace.test.configs:
Key With Whitespace: "I am a value with whitespace in it"
Seems as through spring can parse this yaml fine, and it is apparently valid yaml. However, spring (SnakeYaml?) removes the spaces in the Key string:
KeyWithWhitespace -> I am a value with whitespace in it
An easy solution is to designate a special character for space and replace it within the application, but I was wondering if spring already handled this in some fashion? Perhaps there is a way to escape a space in the config in such a way that spring (SnakeYaml?) knows the we want to keep it, or perhaps there is a way to configure this?
For the sake of completeness I've tried using single and double quotations as well as \s \b.
Update:
After some additional research I found an example from SnakeYaml repository that seems to indicate that what I'm looking for should be possible: https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-block-mappings
Specifically this example:
# YAML
base armor class: 0
base damage: [4,4]
plus to-hit: 12
plus to-dam: 16
plus to-ac: 0
# Java
{'plus to-hit': 12, 'base damage': [4, 4], 'base armor class': 0, 'plus to-ac': 0, 'plus to-dam': 16}
In the example the spaces persist in the keys. Unfortunately, I'm at a loss with regard to figuring out where the whitespace is actually getting removed.

For map keys with special characters you need to surround the key with '[]' for the key to be used as specified.
So, in your case it will be
whitespace.test.configs:
'[Key With Whitespace]': "I am a value with whitespace in it"

The new binder is much stricter about property names which means you need to surround them in square brackets. Try the following:
shiro:
testMap:
"[/test1]": test1
"[/test2]": test2

Related

Preserve leading space in application yaml properties

I've got a list of properties in yml file
foo:
bar: One., Two., Three
when converting them to list
#Value("\${foo.bar}")
public var listOfBar: List<String> = mutableListOf()
Leading spaces are trimmed so I get
"One." "Two." "Three.", but what I need is " One." " Two." " Three." with spaces before each. Putting '\u0020' in front didn't helped, it got trimmed anyway.
Simply use " around your values:
foo:
bar: " One."," Two."," Three"
Also you can use the explicit list format:
foo:
bar:
- " One."
- " Two."
- " Three"
When you expect List<String> or String[], Spring will split the input string value using , as separator.
To produce the string you want, you need to have the whitespace within quotes (otherwise it is ignored as per the yaml syntax):
foo:
bar: " One., Two., Three"
However, the Spring default converter may call trim() on every token (I don't remember exactly if this is actually the case) simply dropping all your leadin/trailing spaces anyway.
In this case, you may want to register a different converter that doesn't trim or -- far better -- just take the string and split it yourself.
I ended up doing this. And it's worked
#Value("#{'\${foo.bar}'.split(',')}")
public var listOfBar: List<String> = mutableListOf()
and surrounded properties with "
foo:
bar: " One., Two., Three"
Removing spaces like this will break the purpose of trim() for yaml file.
Though I don't understand the use-case in which you may require this. but, I can suggest to use a custom pattern to achieve this as follows:
You can have tokens for spaces required in yaml file:
foo:
bar: $__$One., Two., Three$_$
Have a different class just to retrieve the configs:
public class Configs {
#Value("${foo.bar}")
private List<String> yourList;
public List<String> getYourList(){
// before returning, replace $_$ with space in yourList
}
Use it in your code
class UseHere {
#Autowired
private Configs configs;
...
// read as follows
configs.getYourList().get(0);
...
}

Using SymmetricDS to connect to a firebird database [duplicate]

I am using Java Properties to read a properties file. Everything is working fine, but Properties silently drops the backslashes.
(i.e.)
original: c:\sdjf\slkdfj.jpg
after: c:sdjfslkdfj.jpg
How do I make Properties not do this?
I am using the code prop.getProperty(key)
I am getting the properties from a file, and I want to avoid adding double backslashes
It is Properties.load() that's causing the problem that you are seeing as backslash is used for a special purpose.
The logical line holding all the data
for a key-element pair may be spread
out across several adjacent natural
lines by escaping the line terminator
sequence with a backslash character,
\.
If you are unable to use CoolBeans's suggestion then what you can do is read the property file beforehand to a string and replace backslash with double-backslash and then feed it to Properties.load()
String propertyFileContents = readPropertyFileContents();
Properties properties = new Properties();
properties.load(new StringReader(propertyFileContents.replace("\\", "\\\\")));
Use double backslashes c:\\sdjf\\slkdfj.jpg
Properties props = new Properties();
props.setProperty("test", "C:\\dev\\sdk\\test.dat");
System.out.println(props.getProperty("test")); // prints C:\dev\sdk\test.dat
UPDATE CREDIT to #ewh below. Apparently, Windows recognises front slashes. So, I guess you can have your users write it with front slashes instead and if you need backslashes afterwards you can do a replace. I tested this snippet below and it works fine.
Properties props = new Properties();
props.setProperty("test", "C:/dev/sdk/test.dat");
System.out.println(props.getProperty("test")); // prints C:/dev/sdk/test.dat
Use forward slashes. There is never a need in Java to use a backslash in a filename.
In case you really need a backslash in a properties file that will be loaded (like for a property that is not a file path) put \u005c for each backslash character.
The backslash is treated specially in properties files as indicated in the document provided by #unhillbilly.
#EJP: Backslash is definitely needed if, for example, you wanted to store an NTLM login id in a properties file, where the format is DOMAIN\USERNAME with a backslash. This type of property is not a filename so forward slashes will not work.
Edit: #Max Nanasy: From the document (java.util.Properties load javadoc) mentioned above (emphasis mine)
The method does not treat a backslash character, '\', before a non-valid escape character as an error; the backslash is silently dropped. For example, in a Java string the sequence "\z" would cause a compile time error. In contrast, this method silently drops the backslash. Therefore, this method treats the two character sequence "\b" as equivalent to the single character 'b'
For me, I always had trouble with backslashes in the properties file (even with double backslash '\\') unless I specified the unicode.
Replace \ with \\ as below:
c:\sdjf\slkdfj.jpg
to
c:\\sdjf\\slkdfj.jpg
In addition to Bala R's answer I have the following solution to even keep the newline-semantic of backslashes at the end of a line.
Here is my code:
private static Reader preparePropertyFile(File file) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(file));
StringBuilder result = new StringBuilder();
String line;
boolean endingBackslash = false;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (endingBackslash) {
// if the line is empty, is a comment or holds a new property
// definition the backslash found at the end of the previous
// line is not for a multiline property value.
if (line.isEmpty()
|| line.startsWith("#")
|| line.matches("^\\w+(\\.\\w+)*=")) {
result.append("\\\\");
}
}
// if a backslash is found at the end of the line remove it
// and decide what to do depending on the next line.
if (line.endsWith("\\")) {
endingBackslash = true;
line = line.substring(0, line.length() - 1);
} else {
endingBackslash = false;
}
result.append(line.replace("\\", "\\\\"));
}
if (endingBackslash) {
result.append("\\\\");
}
return new StringReader(result.toString());
}
private static Properties getProperties(File file) throws IOException {
Properties result = new Properties();
result.load(preparePropertyFile(file));
return result;
}
The following code will help :
BufferedReader metadataReader = new BufferedReader(new InputStreamReader(new FileInputStream("migrateSchemaGenProps.properties")));
Properties props = new Properties();
props.load(new StringReader(IOUtils.getStringFromReader(metadataReader).replace("\\", "/")));
It is not realy a good thing to use backslashes in a property-file, as they are the escape character.
Nevertheless: a Windows user will trend to use them in any path... Therefore, in a single line thanks apache common IO:
params.load(new StringReader(IOUtils.toString(paramFile.toURI(), null).replaceAll("\\\\", "/")));
you triple use the backslash to get one:
for example:
key=value1\\value2
in the properties file will turn to
key=value1\value2
in the java Properties object

Jackrabbit XPath Query: UUID with leading number in path

I have what I think is an interesting problem executing queries in Jackrabbit when a node in the query path is a UUID that start with a number.
For example, this query work fine as the second node starts with a letter, 'f':
/*/JCP/feeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence]
This query however does not, if the first 'f' is replaced with '2':
/*/JCP/2eeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence]
The exception:
Encountered "-" at line 1, column 26.
Was expecting one of:
<IntegerLiteral> ...
<DecimalLiteral> ...
<DoubleLiteral> ...
<StringLiteral> ...
... rest omitted for brevity ...
for statement: for $v in /*/JCP/2eeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence] return $v
My code in general
def queryString = queryFor path
def queryManager = session.workspace.queryManager
def query = queryManager.createQuery queryString, Query.XPATH // fails here
query.execute().nodes
I'm aware my query, with the leading asterisk, may not be the best, but I'm just starting out with querying in general. Maybe using another language other than XPATH might work.
I tried the advice in this post, adding a save before creating the query, but no luck
Jackrabbit Running Queries against UUID
Thanks in advance for any input!
A solution that worked was to try and properly escape parts of the query path, namely the individual steps used to build up the path into the repository. The exception message was somewhat misleading, at least to me, as in made me think that the hyphens were part of the root cause. The root problem was that the leading number in the node name created an illegal XPATH query as suggested above.
A solution in this case is to encode the individual steps into the path and build the rest of the query. Resulting in the leading number only being escaped:
/*/JCP/_x0032_eeadeaf-1dae-427f-bf4e-842b07965a93//*[#sequence]
Code that represents a list of steps or a path into the Jackrabbit repository:
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.util.ISO9075;
class Path {
List<String> steps; //...
public String asQuery() {
return steps.size() > 0 ? "/*" + asPathString(encodedSteps()) + "//*" : "//*";
}
private String asPathString(List<String> steps) {
return '/' + StringUtils.join(steps, '/');
}
private List<String> encodedSteps() {
List<String> encodedSteps = new ArrayList<>();
for (String step : steps) {
encodedSteps.add(ISO9075.encode(step));
}
return encodedSteps;
}
}
Some more notes:
If we escape more of the query string as in:
/_x002a_/JCP/_x0032_eeadeaf-1dae-427f-bf4e-842b07965a93//_x002a_[#sequence]
Or the original path encoded as a whole as in:
_x002f_a_x002f_fffe4dcf0-360c-11e4-ad80-14feb59d0ab5_x002f_2cbae0dc-35e2-11e4-b5d6-14feb59d0ab5_x002f_c
The queries do not produce the wanted results.
Thanks to #matthias_h and #LarsH
An XML element name cannot start with a digit. See the XML spec's rules for STag, Name, and NameStartChar. Therefore, the "XPath expression"
/*/JCP/2eeadeaf-1dae-427f-bf4e-842b07965a93/label//*[#sequence]
is illegal, because the name test 2eead... isn't a legal XML name.
As such, you can't just use any old UUID as an XML element name nor as a name test in XPath. However if you put a legal NameStartChar on the front (such as _), you can probably use any UUID.
I'm not clear on whether you think you already have XML data with an element named <2eead...> (and are trying to query that element's descendants); if so, whatever tool produced it is broken, as it emits illegal XML. On the other hand if the <2eead...> is something that you yourself are creating, then presumably you have the option of modifying the element name to be a legal XML name.

Parsing double superCSV with comma as decimal separator?

I Want to parse a double with comma as decimal separator (',' instead of '.') using SuperCSV CellProcessor
I want to parse the first element (0,35) to Double
0,35;40000,45
I have tried something like that :
/** FRENCH_SYMBOLS */
private static final DecimalFormatSymbols FRENCH_SYMBOLS = new DecimalFormatSymbols(Locale.FRANCE);
DecimalFormat df = new DecimalFormat();
df.setDecimalFormatSymbols(FRENCH_SYMBOLS);
final CellProcessor[] processors = new CellProcessor[] {
new NotNull(new ParseDouble(new FmtNumber(df))),
new NotNull(new ParseBigDecimal(FRENCH_SYMBOLS)) };
ParseBigDecimal works just fine but the parseDouble doesn't seems to work, it gives me an exception : org.supercsv.exception.SuperCsvCellProcessorException: '0,35' could not be parsed as a Double
You're totally correct - ParseDouble doesn't support a French-style decimal separator (comma), but ParseBigDecimal does. If you think this is a useful feature, why not submit a feature request.
The simplest workaround is to simply chain a StrReplace before the ParseDouble to convert the comma to full stop.
new StrReplace(",", ".", new ParseDouble())
Alternatively, you could write a custom cell processor that either:
parses a Double (with a configurable decimal separator)
converts a BigDecimal to a Double (calling doubleValue()) - this can then be chained after your new ParseBigDecimal(FRENCH_SYMBOLS)
Oh, and in future you might want to mention that your file is semi-colon separated and you've set up Super CSV with CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE :)

Freemarker - replace & with &

If i have the & symbol in some field (from a db, cannot be changed), and i want to display this via freemarker... but have the display (from freemarker) read &, what is the way to do so?
To reiterate, I cannot change the value before hand (or at least, I don't want to), i'd like freemarker to "unmark" &.
To double re-iterate, this is a value that is being placed with a lot of other xml. The value itself is displayed on its own, surrouded by tags... so something like
<someTag>${wheeeMyValueWithAnAmpersand}<someTag>
As a result, i don't want all ampersands escaped, or the xml will look funny... just that one in the interpolation.
Oh goodness.
I see the problem: the code was written like this:
<#escape x as x?xml>
<#import "small.ftl" as my>
<#my.macro1/>
</#escape>
and at which i'd assumed that the excape would excape all the calls within it - it is certainly what the documentation sort of implies
http://freemarker.org/docs/ref_directive_escape.html
<#assign x = "<test>"> m1>
m1: ${x}
</#macro>
<#escape x as x?html>
<#macro m2>m2: ${x}</#macro>
${x}
<#m1/>
</#escape>
${x}
<#m2/>
the output will be:
<test>
m1: <test>
<test>
m2: <test>
However it appears that when you import the file, then this isn't the case, and the escape... escapes!
SOLUTION:
http://watchitlater.com/blog/2011/10/default-html-escape-using-freemarker/
the above link details how to solve the problem. In effect, it comes down to loading a different FreemakerLoader, one that wraps all templates with an escape tag.
class SomeCoolClass implements TemplateLoader {
//other functions here
#Override
public Reader getReader(Object templateSource, String encoding) throws IOException {
Reader reader = delegate.getReader(templateSource, encoding);
try {
String templateText = IOUtils.toString(reader);
return new StringReader(ESCAPE_PREFIX + templateText + ESCAPE_SUFFIX);
} finally {
IOUtils.closeQuietly(reader);
}
}
which is a snippet from the link above. You create the class with the existing templateLoader, and just defer all the required methods to that.
Starting from FreeMarker 2.3.24 no TemplateLoader "hack" is needed anymore. There's a setting called output_format, which specifies if and what escaping is needed. This can be configured both globally, and/or per-template-name-pattern utilizing the template_configurations setting. The recommend way of doing this is even simpler (from the manual):
[...] if the
recognize_standard_file_extensions setting is true (which is the
default with the incompatible_improvements setting set to 2.3.24 or
higher), templates whose source name ends with ".ftlh" gets "HTML"
output format, and those with ".ftlx" get "XML" output format

Resources