プロパティ、配列、リスト、マップ、インデクサー

Spring 式言語は、オブジェクトグラフのナビゲートとさまざまな構造へのインデックス作成をサポートします。

Java で配列の n 番目の要素にアクセスする場合など、数値インデックス値はゼロから始まります。
null セーフ演算子を使用してオブジェクトグラフをナビゲートし、さまざまな構造にインデックスを付ける方法の詳細については、セーフナビゲーション演算子セクションを参照してください。

プロパティナビゲーション

ネストされたプロパティ値を示すピリオドを使用して、オブジェクトグラフ内のプロパティ参照を移動できます。Inventor クラスのインスタンス、pupin および tesla には、例で使用されているクラスセクションにリストされているデータが入力されています。オブジェクトグラフを下に移動して、Tesla の生年と Pupin の出生地を取得するには、次の式を使用します。

  • Java

  • Kotlin

// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);

// evaluates to "Smiljan"
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
// evaluates to 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int

// evaluates to "Smiljan"
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String

プロパティ名の最初の文字では、大文字と小文字を区別しないことが許可されています。上記の例の式は、それぞれ Birthdate.Year + 1900 および PlaceOfBirth.City と書くことができます。さらに、プロパティには、オプションでメソッド呼び出しを介してアクセスできます(たとえば、placeOfBirth.city ではなく getPlaceOfBirth().getCity())。

配列とコレクションへのインデックス作成

配列またはコレクション (たとえば、Set または List) の n 番目の要素は、次の例に示すように、角括弧 表記を使用して取得できます。

インデックス付きコレクションが java.util.List の場合、n 番目の要素は list.get(n) を介して直接アクセスされます。

その他の型の Collection の場合、n 番目の要素にアクセスするには、Iterator を使用してコレクションを反復処理し、検出された n 番目の要素を返します。

  • Java

  • Kotlin

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// Inventions Array

// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
		context, tesla, String.class);

// Members List

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("members[0].name").getValue(
		context, ieee, String.class);

// List and Array Indexing

// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
		context, ieee, String.class);
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()

// Inventions Array

// evaluates to "Induction motor"
val invention = parser.parseExpression("inventions[3]").getValue(
		context, tesla, String::class.java)

// Members List

// evaluates to "Nikola Tesla"
val name = parser.parseExpression("members[0].name").getValue(
		context, ieee, String::class.java)

// List and Array Indexing

// evaluates to "Wireless communication"
val invention = parser.parseExpression("members[0].inventions[6]").getValue(
		context, ieee, String::class.java)

文字列のインデックス

次の例に示すように、角括弧 内のインデックスを指定することで、文字列の n 番目の文字を取得できます。

文字列の n 番目の文字は、java.lang.Character ではなく java.lang.String として評価されます。
  • Java

  • Kotlin

// evaluates to "T" (8th letter of "Nikola Tesla")
String character = parser.parseExpression("members[0].name[7]")
		.getValue(societyContext, String.class);
// evaluates to "T" (8th letter of "Nikola Tesla")
val character = parser.parseExpression("members[0].name[7]")
		.getValue(societyContext, String::class.java)

マップへのインデックス作成

マップの内容は、角括弧 内のキー値を指定することによって取得されます。次の例では、officers マップのキーは文字列であるため、'president' などの文字列リテラルを指定できます。

  • Java

  • Kotlin

// Officer's Map

// evaluates to Inventor("Pupin")
Inventor pupin = parser.parseExpression("officers['president']")
		.getValue(societyContext, Inventor.class);

// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city")
		.getValue(societyContext, String.class);

String countryExpression = "officers['advisors'][0].placeOfBirth.country";

// setting values
parser.parseExpression(countryExpression)
		.setValue(societyContext, "Croatia");

// evaluates to "Croatia"
String country = parser.parseExpression(countryExpression)
		.getValue(societyContext, String.class);
// Officer's Map

// evaluates to Inventor("Pupin")
val pupin = parser.parseExpression("officers['president']")
		.getValue(societyContext, Inventor::class.java)

// evaluates to "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city")
		.getValue(societyContext, String::class.java)

val countryExpression = "officers['advisors'][0].placeOfBirth.country"

// setting values
parser.parseExpression(countryExpression)
		.setValue(societyContext, "Croatia")

// evaluates to "Croatia"
val country = parser.parseExpression(countryExpression)
		.getValue(societyContext, String::class.java)

オブジェクトへのインデックス作成

オブジェクトのプロパティは、角括弧 内のプロパティ名を指定することによって取得できます。これは、キーに基づいてマップの値にアクセスすることに似ています。次の例は、オブジェクトにインデックスを付けて特定のプロパティを取得する方法を示しています。

  • Java

  • Kotlin

// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("#root['name']")
		.getValue(context, tesla, String.class);
// Create an inventor to use as the root context object.
val tesla = Inventor("Nikola Tesla")

// evaluates to "Nikola Tesla"
val name = parser.parseExpression("#root['name']")
		.getValue(context, tesla, String::class.java)

カスタム構造へのインデックス作成

Spring Framework 6.2 以降、Spring 式言語は、開発者が EvaluationContext を使用して IndexAccessor を実装および登録できるようにすることで、カスタム構造へのインデックス作成をサポートしています。カスタムインデックスアクセサーに依存する式のコンパイルをサポートする場合、そのインデックスアクセサーは CompilableIndexAccessor SPI を実装する必要があります。

一般的なユースケースをサポートするために、Spring は、リフレクションを使用してターゲットオブジェクトのインデックス構造から読み取り、オプションで書き込みを行う柔軟な IndexAccessor である組み込みの ReflectiveIndexAccessor を提供します。インデックス構造には、public 読み取りメソッド (読み取り時) または public 書き込みメソッド (書き込み時) を介してアクセスできます。読み取りメソッドと書き込みメソッドの関連は、インデックス構造の一般的な実装に適用できる規則に基づいています。

ReflectiveIndexAccessor は、読み取りアクセス用のバイトコードへのコンパイルをサポートするために、CompilableIndexAccessor も実装しています。ただし、コンパイルが成功するには、構成された読み取りメソッドが public クラスまたは public インターフェースを介して呼び出せる必要があることに注意してください。

次のコードリストは、マップのように動作しますが、java.util.Map インターフェースを実装しない Color 列挙型と FruitMap 型を定義します。SpEL 式内で FruitMap にインデックスを付ける場合は、IndexAccessor を登録する必要があります。

public enum Color {
	RED, ORANGE, YELLOW
}
public class FruitMap {

	private final Map<Color, String> map = new HashMap<>();

	public FruitMap() {
		this.map.put(Color.RED, "cherry");
		this.map.put(Color.ORANGE, "orange");
		this.map.put(Color.YELLOW, "banana");
	}

	public String getFruit(Color color) {
		return this.map.get(color);
	}

	public void setFruit(Color color, String fruit) {
		this.map.put(color, fruit);
	}
}

FruitMap の読み取り専用 IndexAccessor は、new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit") を介して作成できます。そのアクセサーが登録され、FruitMap が #fruitMap という名前の変数として登録されている場合、SpEL 式 #fruitMap[T(example.Color).RED] は "cherry" に評価されます。

FruitMap の読み取り / 書き込み IndexAccessor は、new ReflectiveIndexAccessor(FruitMap.class, Color.class, "getFruit", "setFruit") を介して作成できます。そのアクセサーを登録し、FruitMap を #fruitMap という名前の変数として登録すると、SpEL 式 #fruitMap[T(example.Color).RED] = 'strawberry' を使用して、赤色のフルートマッピングを "cherry" から "strawberry" に変更できます。

次の例は、ReflectiveIndexAccessor を登録して FruitMap にインデックスを付け、次に SpEL 式内で FruitMap にインデックスを付けるようにする方法を示しています。

  • Java

  • Kotlin

// Create a ReflectiveIndexAccessor for FruitMap
IndexAccessor fruitMapAccessor = new ReflectiveIndexAccessor(
		FruitMap.class, Color.class, "getFruit", "setFruit");

// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor);

// Register the fruitMap variable
context.setVariable("fruitMap", new FruitMap());

// evaluates to "cherry"
String fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
		.getValue(context, String.class);
// Create a ReflectiveIndexAccessor for FruitMap
val fruitMapAccessor = ReflectiveIndexAccessor(
		FruitMap::class.java, Color::class.java, "getFruit", "setFruit")

// Register the IndexAccessor for FruitMap
context.addIndexAccessor(fruitMapAccessor)

// Register the fruitMap variable
context.setVariable("fruitMap", FruitMap())

// evaluates to "cherry"
val fruit = parser.parseExpression("#fruitMap[T(example.Color).RED]")
	.getValue(context, String::class.java)