EL map lookup using string key containing single quote - spring

I have a Java HashMap<String,String> that is being provided to a JSP page (itemTypesByKey) via a ModelAndView from Spring, and a separate structure containing a list of keys. (These keys may or may not result in a valid lookup.) I enumerate over these keys, and snag values from the map.
Map<String, String> itemTypes = new HashMap<>();
itemTypes.put("NormalValue", "INGREDIENT");
itemTypes.put("Apostro'Value", "INGREDIENT");
ModelAndView mav = ...
mav.addObject("itemTypesByKey", itemTypes);
The problem I'm seeing, is that if my key contains an apostrophe, the lookup fails.
My code (for debugging purposes):
Key Name: ${currentKeyName}
Not Empty: ${not empty itemTypesByKey[currentKeyName]}
Type: ${itemTypesByKey[currentKeyName]}
All Suite Types: ${itemTypesByKey}
Ends up printing, for two separate keys:
Key Name: NormalValue
Not Empty: true
Type: INGREDIENT
All Suite Types: {NormalValue=INGREDIENT, Apostro'Value=INGREDIENT}
Key Name: Apostro'Value
Not Empty: false
Type:
All Suite Types: {NormalValue=INGREDIENT, Apostro'Value=INGREDIENT}
I understand escaping apostrophe literals when they're used to lookup a value, but why does this fail if my key contains an apostrophe? How can I safely perform a lookup in my map regardless of whether the value of my key contains special characters?
Edit: I'm using Spring version 3.1.4 (mvc, expression, etc.), and this issue does not affect keys containing exclamation points, or other special characters I could think to test.

Below are some tests alongside results. In order to use an apostrophe inside of a key you have to use two single quotes. On top of that, a key that has a single quote in it or special characters (exclamation, double quotes, etc...) needs to have single quotes surrounding it. Bottom line is surround your keys with single quotes & use two single quotes if you have a single quote in your key.
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
Map<String,String> itemTypes = (Map) parser.parseExpression("{NormalValue:'INGREDIENT',ApostroValue:'INGREDIENT'}").getValue();
for (Map.Entry<String, String> e : itemTypes.entrySet())
System.out.println(e.getKey() + ": " + e.getValue());
}
Prints keys & values fine
parseExpression("{NormalValue:'INGREDIENT',Apostro'Value:'INGREDIENT'}")
Exception in thread "main" org.springframework.expression.spel.SpelParseException: EL1046E:(pos 51): Cannot find terminating for string
parseExpression("{NormalValue:'INGREDIENT',Apostro''Value:'INGREDIENT'}")
Exception in thread "main" org.springframework.expression.spel.SpelParseException: EL1043E:(pos 33): Unexpected token. Expected 'colon(:)' but was 'literal_string'
parseExpression("{NormalValue:'INGREDIENT','Apostro''Value':'INGREDIENT'}")
Prints fine
parseExpression("{NormalValue:'INGREDIENT',Apostro\"Value:'INGREDIENT'}")
Exception in thread "main" org.springframework.expression.spel.SpelParseException: EL1045E:(pos 33): Cannot find terminating " for string
parseExpression("{NormalValue:'INGREDIENT','Apostro\"Value':'INGREDIENT'}")
Prints fine
parseExpression("{NormalValue:'INGREDIENT',Apostro!Value:'INGREDIENT'}")
Exception in thread "main" org.springframework.expression.spel.SpelParseException: EL1043E:(pos 33): Unexpected token. Expected 'colon(:)' but was 'not(!)'
parseExpression("{NormalValue:'INGREDIENT','Apostro!Value':'INGREDIENT'}")
Prints fine

Related

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

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

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

findelement is not throwing NoSuchElementException

I want to write a test to check if a webelement with a specified text is not present on a page. This is the code for the method doing the job:
public boolean checkOfAanvraagIsOpgevoerd (String titel)
{
String quote = "\"";
String titelMetQuotes = quote + titel +quote;
titelMetQuotes = "dierdieboeboe";
boolean isOpgevoerd=false;
try {
driver.findElement(By.xpath(".//*[#id='listRequests']//h4/a[contains(text(),"+titelMetQuotes+")]"));
isOpgevoerd=true;
} catch (NoSuchElementException NE) {
NE.printStackTrace();
}
return isOpgevoerd;
}
Although I'm absolutely sure that there is no a tag on the page wich contains the text "dierdieboeboe" still the catch block is skipped. When I replace for instance h4 in h5 in the xpath expression the NoSuchElementException is thrown as expected. It seems that the contains part in the expression is ignored.
Try this (note the single quotes around the actual text):
By.xpath("//*[#id='listRequests']//h4/a[contains(text(),'"+titelMetQuotes+"')]")
contains is a function that takes two strings. Hence the text of your variable titelMetQuotes needs to be quoted. Obviously, in this case it is easier to use single quotes.
Additionally, the variable name (titel with quotes) is quite misleading because it actually has no quotes for another flaw in the code:
String titelMetQuotes = quote + titel +quote;
titelMetQuotes = "dierdieboeboe";
The second line simply overwrites the quoted title with a non quoted string.
Finally, you don't need the leading dot in your xpath expression in order to locate the first element of any kind with id listRequests

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 :)

Resources