Add support for inline maps in SpEL expressions
This commit introduces the ability to specify an inline map in
an expression. The syntax is similar to inline lists and of
the form: "{key:value,key2:value}". The keys can optionally
be quoted. The documentation is also updated with information
on the syntax.
Issue: SPR-9472
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -119,7 +119,7 @@ public class InlineList extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Object> getConstantValue() {
|
||||
public List<Object> getConstantValue() {
|
||||
return (List<Object>) this.constant.getValue();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
|
||||
/**
|
||||
* Represent a map in an expression, e.g. '{name:'foo',age:12}'
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 4.1
|
||||
*/
|
||||
public class InlineMap extends SpelNodeImpl {
|
||||
|
||||
// if the map is purely literals, it is a constant value and can be computed and cached
|
||||
TypedValue constant = null;
|
||||
|
||||
public InlineMap(int pos, SpelNodeImpl... args) {
|
||||
super(pos, args);
|
||||
checkIfConstant();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If all the components of the list are constants, or lists/maps that themselves contain constants, then a constant list
|
||||
* can be built to represent this node. This will speed up later getValue calls and reduce the amount of garbage
|
||||
* created.
|
||||
*/
|
||||
private void checkIfConstant() {
|
||||
boolean isConstant = true;
|
||||
for (int c = 0, max = getChildCount(); c < max; c++) {
|
||||
SpelNode child = getChild(c);
|
||||
if (!(child instanceof Literal)) {
|
||||
if (child instanceof InlineList) {
|
||||
InlineList inlineList = (InlineList) child;
|
||||
if (!inlineList.isConstant()) {
|
||||
isConstant = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (child instanceof InlineMap) {
|
||||
InlineMap inlineMap = (InlineMap) child;
|
||||
if (!inlineMap.isConstant()) {
|
||||
isConstant = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!((c%2)==0 && (child instanceof PropertyOrFieldReference))) {
|
||||
isConstant = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isConstant) {
|
||||
Map<Object,Object> constantMap = new LinkedHashMap<Object,Object>();
|
||||
int childcount = getChildCount();
|
||||
for (int c = 0; c < childcount; c++) {
|
||||
SpelNode keyChild = getChild(c++);
|
||||
SpelNode valueChild = getChild(c);
|
||||
Object key = null;
|
||||
Object value = null;
|
||||
if ((keyChild instanceof Literal)) {
|
||||
key = ((Literal) keyChild).getLiteralValue().getValue();
|
||||
}
|
||||
else if (keyChild instanceof PropertyOrFieldReference) {
|
||||
key = ((PropertyOrFieldReference) keyChild).getName();
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (valueChild instanceof Literal) {
|
||||
value = ((Literal) valueChild).getLiteralValue().getValue();
|
||||
}
|
||||
else if (valueChild instanceof InlineList) {
|
||||
value = ((InlineList) valueChild).getConstantValue();
|
||||
}
|
||||
else if (valueChild instanceof InlineMap) {
|
||||
value = ((InlineMap) valueChild).getConstantValue();
|
||||
}
|
||||
constantMap.put(key, value);
|
||||
}
|
||||
this.constant = new TypedValue(Collections.unmodifiableMap(constantMap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException {
|
||||
if (this.constant != null) {
|
||||
return this.constant;
|
||||
}
|
||||
else {
|
||||
Map<Object, Object> returnValue = new LinkedHashMap<Object, Object>();
|
||||
int childcount = getChildCount();
|
||||
for (int c = 0; c < childcount; c++) {
|
||||
// TODO allow for key being PropertyOrFieldReference like Indexer on maps
|
||||
SpelNode keyChild = getChild(c++);
|
||||
Object key = null;
|
||||
if (keyChild instanceof PropertyOrFieldReference) {
|
||||
PropertyOrFieldReference reference = (PropertyOrFieldReference) keyChild;
|
||||
key = reference.getName();
|
||||
}
|
||||
else {
|
||||
key = keyChild.getValue(expressionState);
|
||||
}
|
||||
Object value = getChild(c).getValue(expressionState);
|
||||
returnValue.put(key, value);
|
||||
}
|
||||
return new TypedValue(returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append('{');
|
||||
int count = getChildCount();
|
||||
for (int c = 0; c < count; c++) {
|
||||
if (c > 0) {
|
||||
s.append(',');
|
||||
}
|
||||
s.append(getChild(c++).toStringAST());
|
||||
s.append(':');
|
||||
s.append(getChild(c).toStringAST());
|
||||
}
|
||||
s.append('}');
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this list is a constant value
|
||||
*/
|
||||
public boolean isConstant() {
|
||||
return this.constant != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<Object,Object> getConstantValue() {
|
||||
return (Map<Object,Object>) this.constant.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import org.springframework.expression.spel.ast.FunctionReference;
|
||||
import org.springframework.expression.spel.ast.Identifier;
|
||||
import org.springframework.expression.spel.ast.Indexer;
|
||||
import org.springframework.expression.spel.ast.InlineList;
|
||||
import org.springframework.expression.spel.ast.InlineMap;
|
||||
import org.springframework.expression.spel.ast.Literal;
|
||||
import org.springframework.expression.spel.ast.MethodReference;
|
||||
import org.springframework.expression.spel.ast.NullLiteral;
|
||||
@@ -515,7 +516,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
|| maybeEatIndexer()) {
|
||||
return pop();
|
||||
}
|
||||
else if (maybeEatInlineList()) {
|
||||
else if (maybeEatInlineListOrMap()) {
|
||||
return pop();
|
||||
}
|
||||
else {
|
||||
@@ -600,7 +601,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
}
|
||||
|
||||
// list = LCURLY (element (COMMA element)*) RCURLY
|
||||
private boolean maybeEatInlineList() {
|
||||
// map = LCURLY (key ':' value (COMMA key ':' value)*) RCURLY
|
||||
private boolean maybeEatInlineListOrMap() {
|
||||
Token t = peekToken();
|
||||
if (!peekToken(TokenKind.LCURLY, true)) {
|
||||
return false;
|
||||
@@ -608,18 +610,53 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
SpelNodeImpl expr = null;
|
||||
Token closingCurly = peekToken();
|
||||
if (peekToken(TokenKind.RCURLY, true)) {
|
||||
// empty list '[]'
|
||||
// empty list '{}'
|
||||
expr = new InlineList(toPos(t.startpos,closingCurly.endpos));
|
||||
}
|
||||
else {
|
||||
List<SpelNodeImpl> listElements = new ArrayList<SpelNodeImpl>();
|
||||
do {
|
||||
listElements.add(eatExpression());
|
||||
}
|
||||
while (peekToken(TokenKind.COMMA,true));
|
||||
|
||||
else if (peekToken(TokenKind.COLON,true)) {
|
||||
closingCurly = eatToken(TokenKind.RCURLY);
|
||||
expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
|
||||
// empty map '{:}'
|
||||
expr = new InlineMap(toPos(t.startpos,closingCurly.endpos));
|
||||
}
|
||||
else {
|
||||
SpelNodeImpl firstExpression = eatExpression();
|
||||
// Next is either:
|
||||
// '}' - end of list
|
||||
// ',' - more expressions in this list
|
||||
// ':' - this is a map!
|
||||
|
||||
if (peekToken(TokenKind.RCURLY)) { // list with one item in it
|
||||
List<SpelNodeImpl> listElements = new ArrayList<SpelNodeImpl>();
|
||||
listElements.add(firstExpression);
|
||||
closingCurly = eatToken(TokenKind.RCURLY);
|
||||
expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
|
||||
}
|
||||
else if (peekToken(TokenKind.COMMA, true)) { // multi item list
|
||||
List<SpelNodeImpl> listElements = new ArrayList<SpelNodeImpl>();
|
||||
listElements.add(firstExpression);
|
||||
do {
|
||||
listElements.add(eatExpression());
|
||||
}
|
||||
while (peekToken(TokenKind.COMMA,true));
|
||||
closingCurly = eatToken(TokenKind.RCURLY);
|
||||
expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
|
||||
|
||||
}
|
||||
else if (peekToken(TokenKind.COLON, true)) { // map!
|
||||
List<SpelNodeImpl> mapElements = new ArrayList<SpelNodeImpl>();
|
||||
mapElements.add(firstExpression);
|
||||
mapElements.add(eatExpression());
|
||||
while (peekToken(TokenKind.COMMA,true)) {
|
||||
mapElements.add(eatExpression());
|
||||
eatToken(TokenKind.COLON);
|
||||
mapElements.add(eatExpression());
|
||||
}
|
||||
closingCurly = eatToken(TokenKind.RCURLY);
|
||||
expr = new InlineMap(toPos(t.startpos,closingCurly.endpos),mapElements.toArray(new SpelNodeImpl[mapElements.size()]));
|
||||
}
|
||||
else {
|
||||
raiseInternalException(t.startpos, SpelMessage.OOD);
|
||||
}
|
||||
}
|
||||
this.constructedNodes.push(expr);
|
||||
return true;
|
||||
@@ -734,7 +771,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
}
|
||||
eatToken(TokenKind.RSQUARE);
|
||||
}
|
||||
if (maybeEatInlineList()) {
|
||||
if (maybeEatInlineListOrMap()) {
|
||||
nodes.add(pop());
|
||||
}
|
||||
push(new ConstructorReference(toPos(newToken), dimensions.toArray(new SpelNodeImpl[dimensions.size()]),
|
||||
|
||||
@@ -1401,6 +1401,11 @@ public class EvaluationTests extends AbstractExpressionTests {
|
||||
expectFailNotAssignable(parser, ctx, "--({1,2,3})");
|
||||
expectFailSetValueNotSupported(parser, ctx, "({1,2,3})=({1,2,3})");
|
||||
|
||||
// InlineMap
|
||||
expectFailNotAssignable(parser, ctx, "({'a':1,'b':2,'c':3})++");
|
||||
expectFailNotAssignable(parser, ctx, "--({'a':1,'b':2,'c':3})");
|
||||
expectFailSetValueNotSupported(parser, ctx, "({'a':1,'b':2,'c':3})=({'a':1,'b':2,'c':3})");
|
||||
|
||||
// BeanReference
|
||||
ctx.setBeanResolver(new MyBeanResolver());
|
||||
expectFailNotAssignable(parser, ctx, "@foo++");
|
||||
|
||||
@@ -93,7 +93,6 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
public void testGetValueFromRootMap() {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
map.put("key", "value");
|
||||
EvaluationContext context = new StandardEvaluationContext(map);
|
||||
|
||||
ExpressionParser spelExpressionParser = new SpelExpressionParser();
|
||||
Expression expr = spelExpressionParser.parseExpression("#root['key']");
|
||||
@@ -168,11 +167,11 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Map getProperties() {
|
||||
public Map<String,String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Map properties) {
|
||||
public void setProperties(Map<String,String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
@@ -198,7 +197,7 @@ public class MapAccessTests extends AbstractExpressionTests {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||
((Map) target).put(name, newValue);
|
||||
((Map<Object,Object>) target).put(name, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.expression.spel.ast.InlineMap;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test usage of inline maps.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 4.1
|
||||
*/
|
||||
public class MapTests extends AbstractExpressionTests {
|
||||
|
||||
// if the list is full of literals then it will be of the type unmodifiableClass
|
||||
// rather than HashMap (or similar)
|
||||
Class<?> unmodifiableClass = Collections.unmodifiableMap(new LinkedHashMap<Object,Object>()).getClass();
|
||||
|
||||
|
||||
@Test
|
||||
public void testInlineMapCreation01() {
|
||||
evaluate("{'a':1, 'b':2, 'c':3, 'd':4, 'e':5}", "{a=1, b=2, c=3, d=4, e=5}", unmodifiableClass);
|
||||
evaluate("{'a':1}", "{a=1}", unmodifiableClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapCreation02() {
|
||||
evaluate("{'abc':'def', 'uvw':'xyz'}", "{abc=def, uvw=xyz}", unmodifiableClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapCreation03() {
|
||||
evaluate("{:}", "{}", unmodifiableClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapCreation04() {
|
||||
evaluate("{'key':'abc'=='xyz'}", "{key=false}", LinkedHashMap.class);
|
||||
evaluate("{key:'abc'=='xyz'}", "{key=false}", LinkedHashMap.class);
|
||||
evaluate("{key:'abc'=='xyz',key2:true}[key]", "false", Boolean.class);
|
||||
evaluate("{key:'abc'=='xyz',key2:true}.get('key2')", "true", Boolean.class);
|
||||
evaluate("{key:'abc'=='xyz',key2:true}['key2']", "true", Boolean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapAndNesting() {
|
||||
evaluate("{a:{a:1,b:2,c:3},b:{d:4,e:5,f:6}}", "{a={a=1, b=2, c=3}, b={d=4, e=5, f=6}}", unmodifiableClass);
|
||||
evaluate("{a:{x:1,y:'2',z:3},b:{u:4,v:{'a','b'},w:5,x:6}}", "{a={x=1, y=2, z=3}, b={u=4, v=[a, b], w=5, x=6}}", unmodifiableClass);
|
||||
evaluate("{a:{1,2,3},b:{4,5,6}}", "{a=[1, 2, 3], b=[4, 5, 6]}", unmodifiableClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapWithFunkyKeys() {
|
||||
evaluate("{#root.name:true}","{Nikola Tesla=true}",LinkedHashMap.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapError() {
|
||||
parseAndCheckError("{key:'abc'", SpelMessage.OOD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelOperatorsIs02() {
|
||||
evaluate("{a:1, b:2, c:3, d:4, e:5} instanceof T(java.util.Map)", "true", Boolean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapAndProjectionSelection() {
|
||||
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.![value>3]", "[false, false, false, true, true, true]", ArrayList.class);
|
||||
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6}.?[value>3]", "{d=4, e=5, f=6}", HashMap.class);
|
||||
evaluate("{a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10}.?[value%2==0]", "{b=2, d=4, f=6, h=8, j=10}", HashMap.class);
|
||||
// TODO this looks like a serious issue (but not a new one): the context object against which arguments are evaluated seems wrong:
|
||||
// evaluate("{a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10}.?[isEven(value) == 'y']", "[2, 4, 6, 8, 10]", ArrayList.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetConstruction01() {
|
||||
evaluate("new java.util.HashMap().putAll({a:'a',b:'b',c:'c'})", null, Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstantRepresentation1() {
|
||||
checkConstantMap("{f:{'a','b','c'}}", true);
|
||||
checkConstantMap("{'a':1,'b':2,'c':3,'d':4,'e':5}", true);
|
||||
checkConstantMap("{aaa:'abc'}", true);
|
||||
checkConstantMap("{:}", true);
|
||||
checkConstantMap("{a:#a,b:2,c:3}", false);
|
||||
checkConstantMap("{a:1,b:2,c:Integer.valueOf(4)}", false);
|
||||
checkConstantMap("{a:1,b:2,c:{#a}}", false);
|
||||
checkConstantMap("{#root.name:true}",false);
|
||||
checkConstantMap("{a:1,b:2,c:{d:true,e:false}}", true);
|
||||
checkConstantMap("{a:1,b:2,c:{d:{1,2,3},e:{4,5,6},f:{'a','b','c'}}}", true);
|
||||
}
|
||||
|
||||
private void checkConstantMap(String expressionText, boolean expectedToBeConstant) {
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
SpelExpression expression = (SpelExpression) parser.parseExpression(expressionText);
|
||||
SpelNode node = expression.getAST();
|
||||
assertTrue(node instanceof InlineMap);
|
||||
InlineMap inlineMap = (InlineMap) node;
|
||||
if (expectedToBeConstant) {
|
||||
assertTrue(inlineMap.isConstant());
|
||||
}
|
||||
else {
|
||||
assertFalse(inlineMap.isConstant());
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testInlineMapWriting() {
|
||||
// list should be unmodifiable
|
||||
evaluate("{a:1, b:2, c:3, d:4, e:5}[a]=6", "[a:1,b: 2,c: 3,d: 4,e: 5]", unmodifiableClass);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -298,30 +298,26 @@ public class ParsingTests {
|
||||
}
|
||||
|
||||
// inline list creation
|
||||
// public void testInlineListCreation01() {
|
||||
// parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
|
||||
// }
|
||||
//
|
||||
// public void testInlineListCreation02() {
|
||||
// parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
|
||||
// }
|
||||
@Test
|
||||
public void testInlineListCreation01() {
|
||||
parseCheck("{1, 2, 3, 4, 5}", "{1,2,3,4,5}");
|
||||
}
|
||||
|
||||
// // inline map creation
|
||||
// public void testInlineMapCreation01() {
|
||||
// parseCheck("#{'key1':'Value 1', 'today':DateTime.Today}");
|
||||
// }
|
||||
//
|
||||
// public void testInlineMapCreation02() {
|
||||
// parseCheck("#{1:'January', 2:'February', 3:'March'}");
|
||||
// }
|
||||
//
|
||||
// public void testInlineMapCreation03() {
|
||||
// parseCheck("#{'key1':'Value 1', 'today':'Monday'}['key1']");
|
||||
// }
|
||||
//
|
||||
// public void testInlineMapCreation04() {
|
||||
// parseCheck("#{1:'January', 2:'February', 3:'March'}[3]");
|
||||
// }
|
||||
@Test
|
||||
public void testInlineListCreation02() {
|
||||
parseCheck("{'abc','xyz'}", "{'abc','xyz'}");
|
||||
}
|
||||
|
||||
// inline map creation
|
||||
@Test
|
||||
public void testInlineMapCreation01() {
|
||||
parseCheck("{'key1':'Value 1','today':DateTime.Today}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineMapCreation02() {
|
||||
parseCheck("{1:'January',2:'February',3:'March'}");
|
||||
}
|
||||
|
||||
// methods
|
||||
@Test
|
||||
|
||||
@@ -11058,6 +11058,7 @@ The expression language supports the following functionality
|
||||
* Bean references
|
||||
* Array construction
|
||||
* Inline lists
|
||||
* Inline maps
|
||||
* Ternary operator
|
||||
* Variables
|
||||
* User defined functions
|
||||
@@ -11562,7 +11563,22 @@ Lists can be expressed directly in an expression using `{}` notation.
|
||||
entirely composed of fixed literals then a constant list is created to represent the
|
||||
expression, rather than building a new list on each evaluation.
|
||||
|
||||
[[expressions-inline-maps]]
|
||||
==== Inline Maps
|
||||
Maps can also be expressed directly in an expression using `{key:value}` notation.
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// evaluates to a Java map containing the two entries
|
||||
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
|
||||
|
||||
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);
|
||||
----
|
||||
`{:}` by itself means an empty map. For performance reasons, if the map is itself composed
|
||||
of fixed literals or other nested constant structures (lists or maps) then a constant map is created
|
||||
to represent the expression, rather than building a new map on each evaluation. Quoting of the map keys
|
||||
is optional, the examples above are not using quoted keys.
|
||||
|
||||
[[expressions-array-construction]]
|
||||
==== Array construction
|
||||
|
||||
Reference in New Issue
Block a user