- PR -

taglibでMapにアクセス

投稿者投稿内容
Jumpin'' Jack Flash
大ベテラン
会議室デビュー日: 2006/01/24
投稿数: 198
投稿日時: 2006-07-11 00:30
ちょっと、意味のわからない現象が出ています。

だいぶ省略しますが、このようなクラスを定義します。

public class Hoge {

private Map data = new HashMap();

public Map getTada() {
return this.data;
}

}

このdataにtaglibでアクセスします。
<c:out value="${hoge.data['key'].value}" />
で、正しい値が表示されます。

しかし、クラスの定義を下記のようにすると、

public class Hoge extends HashMap {

上記taglibでの表示結果は、
空文字列になってしまいます。

public class Hoge implements Map {

としても同様に空文字列になってしまいます。

dataという変数名がいけないのかと思い、
予約語になりそうにない名前をつけてもダメでした。

どうしてでしょうか?

どなたか、見当が付く方がいらっしゃいましたら
教えてください。
uk
ぬし
会議室デビュー日: 2003/05/20
投稿数: 1155
お住まい・勤務地: 東京都
投稿日時: 2006-07-11 11:55
よく状況がわからないのですが、

引用:

しかし、クラスの定義を下記のようにすると、
public class Hoge extends HashMap {


このように変更した後も、

引用:

このdataにtaglibでアクセスします。
<c:out value="${hoge.data['key'].value}" />


このようにして表示しようとしている、ということですか?
クラス定義を変えた後もgetDataというメソッドは存在しているのですか?
Jumpin'' Jack Flash
大ベテラン
会議室デビュー日: 2006/01/24
投稿数: 198
投稿日時: 2006-07-11 12:02
コメントありがとうございます。

変更したのは、「 extends HashMap 」をつけただけで、
getData()メソッドも存在します。

よろしくお願いいたします。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-07-12 22:46
アクセスの仕方を間違えているに一票。

${hoge.data['key'].value}がそもそも何にアクセスしているかですが…。

まず、${hoge}はHttpServletRequest型の暗黙オブジェクトrequestに
"hoge"というキーでsetAttributeされているオブジェクトへのアクセスです。

値を設定しているServlet側の処理が書かれていないので憶測ですが
コード:
Hoge hoge = new Hoge();
// hogeに対するデータ設定処理
Map data = hoge.getData();
data.put("key", new Piyo());
request.setAttribute("hoge", hoge);


とでもなっているのでしょう。

${hoge.data['key']}では、このhogeに対して
getData()メソッド(getTada()と宣言しているのはtypoですかね?)
を呼び出して取得したMapに対しget("key")を呼び出して
得たオブジェクトにアクセスしています。

${hoge.data['key'].value}ではさらにそのオブジェクトの
getValue()メソッドで値を取得しているわけです。

この上記の過程のどこまで正しく動いて、どこから正しくないのかを
探ればバグの原因がつかめるのではないでしょうか。

私の予測ではHoge自身をMapにした際に
コード:
Hoge hoge = new Hoge();
// hogeに対するデータ設定処理
hoge.put("key", new Piyo()); // <- Hoge.dataではなくHogeに設定
request.setAttribute("hoge", hoge);


としているなどのミスがあるのではないか…。

深い階層を持つデータを扱う際はよくやるミスですが、
EL式でアクセスする場合はエラーにならないので
バグがわかりにくいかもしれませんね。
Anthyhime
ぬし
会議室デビュー日: 2002/09/10
投稿数: 437
投稿日時: 2006-07-13 09:45
ELではMapに対し通常のプロパティ名と同様の記法でアクセスできる仕様を提供しています。Mapを実装したHogeに対する

hoge.data['key'].value

の意味は、まず、
hoge.get("data");
となります。get("data")の戻り値がnullなので途中の処理が省略されて、空文字列が返されているのでしょう。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-07-13 10:49
引用:

Anthyhimeさんの書き込み (2006-07-13 09:45) より:
ELではMapに対し通常のプロパティ名と同様の記法でアクセスできる仕様を提供しています。Mapを実装したHogeに対する

hoge.data['key'].value

の意味は、まず、
hoge.get("data");
となります。get("data")の戻り値がnullなので途中の処理が省略されて、空文字列が返されているのでしょう。



上記指摘の動きがちょっと記憶になかったので調べてみました。
http://java.sun.com/products/jsp/syntax/2.0/syntaxref207.html#1010522
言語仕様側では該当の記述がどこなのかよくわからないのですが
(単に私の語学力の問題のようにおもいますけど:-P)
ELのTipには該当のネタがありました。
http://java.sun.com/developer/EJTechTips/2004/tt0126.html
引用:

If the object being accessed is a Map, the "." operator uses the name of the right-hand side as a string literal, and uses it as a key to fetch the result. This is demonstrated in the sample code in the two following expressions. The expressions are equivalent (both reference the host HTTP header):

${header["host"]}
${header.host}



確かに、Mapの場合は"."の後ろのプロパティを文字列リテラルとして
キーとして扱うという動きをするようですね。
いやはや、失礼いたしました。
Jumpin'' Jack Flash
大ベテラン
会議室デビュー日: 2006/01/24
投稿数: 198
投稿日時: 2006-07-13 11:35
皆様、ありがとうございます。
状況が理解できました。

まず、現象を起こす最も簡単なサンプルを提示します。

■Hoge.java
コード:

public class Hoge {

private Map<String, String> data = new HashMap<String, String>();

public Map<String, String> getData() {
return this.data;
}

public void putData(String key, String value) {
this.data.put(key, value);
}

}



■Servlet(こちら、Springを使っています)
コード:

public ModelAndView handleHogeTest(HttpServletRequest request
, HttpServletResponse response)
throws ServletException {

Hoge hoge = new Hoge();
hoge.putData("key", "value");

return new ModelAndView(this.hogeTestView, "hoge", hoge);
}



■JSP
コード:

<%@
page language="java" contentType="text/html; charset=UTF-8"
%><%@
taglib uri="http://java.sun.com/jstl/core" prefix="c"
%>
<c:out value="${hoge.data['key']}" />



これで、"value"と表示されます。
Hogeクラスのシグニチャを
public class Hoge extends HashMap<String, Object> {
に変更すると、"value"は表示されなくなります。

そこで、
引用:

Anthyhimeさんの書き込み (2006-07-13 09:45) より:
ELではMapに対し通常のプロパティ名と同様の記法でアクセスできる仕様を提供しています。Mapを実装したHogeに対する

hoge.data['key'].value

の意味は、まず、
hoge.get("data");
となります。get("data")の戻り値がnullなので途中の処理が省略されて、空文字列が返されているのでしょう。


これを踏まえ、Hoge.javaを変更したら表示されるようになりました。

変更パターン1:コンストラクタでdataをput()
コード:

public Hoge() {
this.put("data", this.data);
}



変更パターン2:getメソッドをオーバーライド
コード:

public Object get(Object key) {
if ("data".equals(key)) {
return this.data;
}
return super.get(key);
}


#なんで、HashMap#get()メソッドのシグニチャは
#V get(Object key) なの?
#V get(K key) じゃないんだろう?

しかし、問題があります。
genericsを使って書いたのはそのためなのですが、
実際のHogeクラスのシグニチャは、
public class Hoge extends HashMap<String, String> {
なのです。
よって、
this.put("data", this.data);

return this.data;
もエラーです。

これを回避する方法を考え中です...
#せめて、HashMap#get()メソッドのシグニチャが
#Object get(K key) か Object get(Object key)
#ならなぁ...


[ メッセージ編集済み 編集者: Jumpin' Jack Flash 編集日時 2006-07-13 11:36 ]

[ メッセージ編集済み 編集者: Jumpin' Jack Flash 編集日時 2006-07-13 11:37 ]
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-07-13 22:04
引用:

Jumpin' Jack Flashさんの書き込み (2006-07-13 11:35) より:
しかし、問題があります。
genericsを使って書いたのはそのためなのですが、
実際のHogeクラスのシグニチャは、
public class Hoge extends HashMap<String, String> {
なのです。
よって、
this.put("data", this.data);

return this.data;
もエラーです。

これを回避する方法を考え中です...
#せめて、HashMap#get()メソッドのシグニチャが
#Object get(K key) か Object get(Object key)
#ならなぁ...



なぜそんなことをしないといけないのでしょうか?

背景が見えませんが、V get(Object key)ではなく、
Object get(Object key)だったらいいな、とおっしゃってますか?
もし、単にvalue側をObject型として扱いたいのであれば
HashMap<String, Object>とすればよいのではないですか?

valueの型を可変にしたいのであれば、
public class Hoge<V> extends HashMap<String, V>
でよいでしょうし。
extends HashMapしている点も含めて、データの構造の設計が
危ういように思えますが…。

スキルアップ/キャリアアップ(JOB@IT)