JSF 转换器可以处理实现相同接口的不同类型的对象吗?
Can JSF converter work with objects of different types which implements the same interface, is it possible?
我有不同的对象,它们都实现相同的接口。 <p:selectCheckboxMenu/>
需要 select 能够使用所有这些对象。这些对象的 默认 值和 selected 值放置在相同的 Map<?,?>
中,很少有这样的 Maps
在另一个 Map
中组合在一起。听起来很复杂,但是请看下面的代码,一切都会很清楚。
当我 select 一个对象时,转换器从 MyBean (CDI bean) 中获取所有对象的列表,通过 uuid
所需的对象和 return 查找它,而不会抛出任何异常.当我尝试使用 selected 对象时,问题就开始了。比如里面的这行代码:
onObjectChange()
方法来自 MyBean
:
List<AllObjects> objects= objectContainer.getControllers().get("Object 1").get("selected");
抛出异常:
java.lang.ClassCastException: [Ljava.lang.Object; incompatible with java.util.List
确实,当我将鼠标悬停在 objectContainer
上时,我看到它包含 selected=[Ljava.lang.Object;@dba1b6e7}
类型的对象但是当我在 Eclipse 的表达式面板中评估同一行代码时,我得到了所需的值:MyObject1@d8f0f5f8
我一般不明白是否可以做我正在做的事情,即很少有具有相同界面的不同对象可以被 select<p:selectCheckboxMenu/>
实现。如果是,为什么我有这个铸造问题?我的同事说这可能是我的 converter
的问题,我倾向于同意她的看法,但不知道是否正确,如果是,如何解决。
更新:看起来问题不在 Converter
内部,但由于我通过 Collection
动态收集 selected 值,作为值<ui:param/>
。我将它作为 List<AllObjects>
传递并作为 Object
返回。然后我可以将它转换为 Object[]
并将其中的每个对象转换为使用内省的适当对象并且它可以工作。但为什么它会改变初始对象?它不应该这样做。
在此先感谢您和我的代码:
这是一个界面:
public interfaces AllObjects{
public String getName();
}
有多个对象,MyObject
,MyObject1
,MyObject2
实现了上面的接口:
public MyObject implements AllObjects{
...
}
这是我的 bean 以及我的对象是如何初始化的:
public MyBean {
Map<String, Map<String,List<AllObjects>>> objectContainer = new LinkedHashMap<String, Map<String,List<AllObjects>>>();
public void init(){
Map<String,List<AllObjects>> nameValuesPairs1 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects1 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects1 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects1);
nameValuesPairs.put("selected",allSelectedObjects1);
Map<String,List<AllObjects>> nameValuesPairs2 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects2 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects2 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects2);
nameValuesPairs.put("selected",allSelectedObjects2);
objectContainer.put("Object 1", nameValuesPairs1);
objectContainer.put("Object 2", nameValuesPairs2);
}
public void onObjectChange(){
...
List<AllObjects> objects= objectContainer .getControllers().get("Object 1").get("selected"); //throws exception
...
}
}
我的 *.xhtml 页面:
<h:panelGroup id="object_panel">
<ui:repeat id="objects_id" var="object" value="#{myBean.objectContainer.entrySet().toArray()}">
<p:selectCheckboxMenu
value="#{object.value['selected']}" label="#{object.key}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.value['default']}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="@this @parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
</h:panelGroup>
和转换器:
public class ChartParameterConverter implements Converter, Serializable {
@Inject
private MyBean myBean;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
AllObjects result = null;
...
//here to result assigned MyObject1 or MyObject2 type depends on condition and it being returned
...
return result;
}
...
}
您可以为每个对象创建一个唯一的元组,该元组必须转换为唯一的字符串。您可以将可能的值放在一个数组中。
static Map<String,Object> uniques = new LinkedHashMap<>();
static{
//you could save the possible values in a Singleton Bean
uniques.put(key,value)...
}
在您的转换器中
getAsString -> Return a Key from Value
getAsObject -> Return a Value by Key
好的,看起来 <ui:repeat>
和 <p:selectCheckboxMenu>
不能很好地与我使用的 DataModel Map<String,Map<String,List<MyObject>>>
一起使用。我按以下方式更改了 DataModel:
public ObjectContainer{
private String name;
private List<MyObject> defaultObjects;
private List<MyObject> selectedObbjects;
}
并将其作为 List<ObjectContainer>
传递给 <ui:param>
。所以我的 *.xhtml 页面看起来像下面这样:
<p:selectCheckboxMenu
value="#{object.selectedObjects}" label="#{object.name}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.defaultObjects}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="@this @parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
现在一切正常。
我扔掉了自定义转换器并使用 Omnifaces's library 中的 SelectItemsConverter
。强烈推荐更改,代码变得更加简单易读。
我有不同的对象,它们都实现相同的接口。 <p:selectCheckboxMenu/>
需要 select 能够使用所有这些对象。这些对象的 默认 值和 selected 值放置在相同的 Map<?,?>
中,很少有这样的 Maps
在另一个 Map
中组合在一起。听起来很复杂,但是请看下面的代码,一切都会很清楚。
当我 select 一个对象时,转换器从 MyBean (CDI bean) 中获取所有对象的列表,通过 uuid
所需的对象和 return 查找它,而不会抛出任何异常.当我尝试使用 selected 对象时,问题就开始了。比如里面的这行代码:
onObjectChange()
方法来自 MyBean
:
List<AllObjects> objects= objectContainer.getControllers().get("Object 1").get("selected");
抛出异常:
java.lang.ClassCastException: [Ljava.lang.Object; incompatible with java.util.List
确实,当我将鼠标悬停在 objectContainer
上时,我看到它包含 selected=[Ljava.lang.Object;@dba1b6e7}
类型的对象但是当我在 Eclipse 的表达式面板中评估同一行代码时,我得到了所需的值:MyObject1@d8f0f5f8
我一般不明白是否可以做我正在做的事情,即很少有具有相同界面的不同对象可以被 select<p:selectCheckboxMenu/>
实现。如果是,为什么我有这个铸造问题?我的同事说这可能是我的 converter
的问题,我倾向于同意她的看法,但不知道是否正确,如果是,如何解决。
更新:看起来问题不在 Converter
内部,但由于我通过 Collection
动态收集 selected 值,作为值<ui:param/>
。我将它作为 List<AllObjects>
传递并作为 Object
返回。然后我可以将它转换为 Object[]
并将其中的每个对象转换为使用内省的适当对象并且它可以工作。但为什么它会改变初始对象?它不应该这样做。
在此先感谢您和我的代码:
这是一个界面:
public interfaces AllObjects{
public String getName();
}
有多个对象,MyObject
,MyObject1
,MyObject2
实现了上面的接口:
public MyObject implements AllObjects{
...
}
这是我的 bean 以及我的对象是如何初始化的:
public MyBean {
Map<String, Map<String,List<AllObjects>>> objectContainer = new LinkedHashMap<String, Map<String,List<AllObjects>>>();
public void init(){
Map<String,List<AllObjects>> nameValuesPairs1 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects1 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects1 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects1);
nameValuesPairs.put("selected",allSelectedObjects1);
Map<String,List<AllObjects>> nameValuesPairs2 = new LinkedHashMap<String,List<AllObjects>>();
List<AllObjects> allSelectedObjects2 = new ArrayList<AllObjects>();
List<AllObjects> allDefaultObjects2 = new ArrayList<AllObjects>();
nameValuesPairs.put("default",allDefaultObjects2);
nameValuesPairs.put("selected",allSelectedObjects2);
objectContainer.put("Object 1", nameValuesPairs1);
objectContainer.put("Object 2", nameValuesPairs2);
}
public void onObjectChange(){
...
List<AllObjects> objects= objectContainer .getControllers().get("Object 1").get("selected"); //throws exception
...
}
}
我的 *.xhtml 页面:
<h:panelGroup id="object_panel">
<ui:repeat id="objects_id" var="object" value="#{myBean.objectContainer.entrySet().toArray()}">
<p:selectCheckboxMenu
value="#{object.value['selected']}" label="#{object.key}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.value['default']}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="@this @parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
</h:panelGroup>
和转换器:
public class ChartParameterConverter implements Converter, Serializable {
@Inject
private MyBean myBean;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
AllObjects result = null;
...
//here to result assigned MyObject1 or MyObject2 type depends on condition and it being returned
...
return result;
}
...
}
您可以为每个对象创建一个唯一的元组,该元组必须转换为唯一的字符串。您可以将可能的值放在一个数组中。
static Map<String,Object> uniques = new LinkedHashMap<>();
static{
//you could save the possible values in a Singleton Bean
uniques.put(key,value)...
}
在您的转换器中
getAsString -> Return a Key from Value
getAsObject -> Return a Value by Key
好的,看起来 <ui:repeat>
和 <p:selectCheckboxMenu>
不能很好地与我使用的 DataModel Map<String,Map<String,List<MyObject>>>
一起使用。我按以下方式更改了 DataModel:
public ObjectContainer{
private String name;
private List<MyObject> defaultObjects;
private List<MyObject> selectedObbjects;
}
并将其作为 List<ObjectContainer>
传递给 <ui:param>
。所以我的 *.xhtml 页面看起来像下面这样:
<p:selectCheckboxMenu
value="#{object.selectedObjects}" label="#{object.name}"
converter="#{myObjectConverter}"
filter="true"
filterMatchMode="startsWith"
panelStyle="width:250px">
<f:selectItems value="#{object.defaultObjects}" var="value" itemValue="#{value}" itemLabel="#{value.name}" />
<p:ajax event="change" process="@this @parent" listener="#{myBean.onObjectChange}"/>
</p:selectCheckboxMenu>
</ui:repeat>
现在一切正常。
我扔掉了自定义转换器并使用 Omnifaces's library 中的 SelectItemsConverter
。强烈推荐更改,代码变得更加简单易读。