【问题标题】:How to write a custom converter for <p:pickList>如何为 <p:pickList> 编写自定义转换器
【发布时间】:2011-04-06 23:33:08
【问题描述】:

在使用使用 POJO 列表的 PrimeFaces 组件时,如何编写自定义转换器?我的特殊问题是&lt;p:pickList&gt;

<p:pickList converter="????" value="#{bean.projects}" var="project" 
                             itemLabel="#{project.name}" itemValue="#{project}">

如果没有转换器,我会得到 java.lang.ClassCastException,因为 JSF 将提交的值设置为未转换的 java.lang.String 提交的值。

【问题讨论】:

    标签: jsf primefaces converter picklist


    【解决方案1】:

    是的,您可以编写一个转换器来序列化/反序列化选择列表中的对象,如下所示:

    @FacesConverter(value="PositionMetricConverter")
    public class PositionMetricConverter implements Converter {
    
        private static final Logger log = Logger.getLogger(PositionMetricConverter.class.getName());
    
        @Override
        public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value) {
            try {
                byte[] data = Base64.decodeBase64(value);
                ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
                Object o = ois.readObject();
                ois.close();
                return o;
            } catch (Exception e) {
                log.log(Level.SEVERE, "Unable to decode PositionMetric!", e);
                return null;
            }
        }
    
        @Override
        public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object value) {
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(value);
                oos.close();
    
                return Base64.encodeBase64String(baos.toByteArray());
            } catch (IOException e) {
                log.log(Level.SEVERE, "Unable to encode PositionMetric!", e);
                return "";
            }
        }
    
    }
    

    然后将此转换器应用到您的选择列表中,如下所示:

    <p:pickList converter="PositionMetricConverter" value="#{bean.positionMetrics}" 
        var="positionMetric" itemLabel="#{positionMetric.name}" itemValue="#{positionMetric}"/>
    

    并确保您的对象是可序列化的。

    【讨论】:

      【解决方案2】:

      是的,有可能:

      public class DocumentSBean sBean implements Serializable{
      
      private List<Document> projects;
      // projects methods...
      // ...
      
      public Converter getDocumentConverter(){
       return docConverter;
      }
      
      private Converter docConverter = new Converter() {
      
              @Override
              public Object getAsObject(FacesContext context, UIComponent component, String value) {
                  return projects.stream().filter(p -> p.getName().equals(value)).findFirst().orElse(null);
              }
      
              @Override
              public String getAsString(FacesContext context, UIComponent component, Object value) {
                  return (value != null)
                          ? ((Document) value).toString()
                          : null;
              }
          };
      }
      
      <p:pickList converter="#{sBean.documentConverter}" value="#...
      

      【讨论】:

        【解决方案3】:

        研究了如何编写自定义转换器后,这里是解决方案。
        1.创建一个实现javax.faces.convert.Converter;的Java Class

        public class ProjectConverter implements Converter{
        
           @EJB
           DocumentSBean sBean;
        
           public ProjectConverter(){
           }
        
           public Object getAsObject(FacesContext context, UIComponent component, String value){
             return sBean.getProjectById(value);
             //If u look below, I convert the object into a unique string, which is its id.
             //Therefore, I just need to write a method that query the object back from the 
             //database if given a id. getProjectById, is a method inside my Session Bean that
             //does what I just described
           }
        
           public String getAsString(FacesContext context, UIComponent component, Object value)     
           {
             return ((Project) value).getId().toString(); //--> convert to a unique string.
           }
        }
        

        2。在faces-config.xml注册您的自定义转换器

        <converter>
            <converter-id>projectConverter</converter-id>
            <converter-class>org.xdrawing.converter.ProjectConverter</converter-class>
        </converter>
        

        3。所以现在在 Primefaces 组件中,你只需执行converter="projectConverter"。注意projectConverter 是我刚刚创建的&lt;convert-id&gt;。所以为了解决我上面的问题,我这样做:

        <p:pickList converter="projectConverter" value="#{bean.projects}" var="project" 
                                    itemLabel="#{project.name}" itemValue="#{project}">
        

        【讨论】:

          【解决方案4】:

          这是可能的,没有其他数据库访问,但我不知道最好的方法。我使用一个非常具体的转换器,仅适用于选择列表。试试这个:

          @FacesConverter(value = "primeFacesPickListConverter")public class PrimeFacesPickListConverter implements Converter {
          @Override
          public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
              Object ret = null;
              if (arg1 instanceof PickList) {
                  Object dualList = ((PickList) arg1).getValue();
                  DualListModel dl = (DualListModel) dualList;
                  for (Object o : dl.getSource()) {
                      String id = "" + ((Project) o).getId();
                      if (arg2.equals(id)) {
                          ret = o;
                          break;
                      }
                  }
                  if (ret == null)
                      for (Object o : dl.getTarget()) {
                          String id = "" + ((Project) o).getId();
                          if (arg2.equals(id)) {
                              ret = o;
                              break;
                          }
                      }
              }
              return ret;
          }
          
          @Override
          public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
              String str = "";
              if (arg2 instanceof Project) {
                  str = "" + ((Project) arg2).getId();
              }
              return str;
          }
          

          和选择列表:

          <p:pickList converter="primeFacesPickListConverter" value="#{bean.projects}" var="project" 
                                  itemLabel="#{project.name}" itemValue="#{project}">
          

          为我工作,需要改进。

          【讨论】:

          • 这是最好的解决方案。在转换器中查询数据库是最糟糕的事情,即将 1000 个项目添加到源列表中,至少包含一个嵌套实体,然后看看会发生什么;)
          • +1 因为这比接受的答案更好。正是我一直在寻找的。​​span>
          • 顺便问一下,你能告诉我你的代码中的Project是什么吗?它是bean中的双重列表吗?或var?
          • "Project" 是来自原始问题的 bean 类,在您的代码中替换为必要的 bean 类。或者在通用接口中抽象“getId()”并将这个转换器重用于另一种情况,如下所示:stackoverflow.com/questions/19048801/…
          • 谢谢@BraullyRocha .. 很棒的答案,美妙的是在这个答案中你使用了 bean 类。
          【解决方案5】:

          这个问题与primefaces无关,只是一般的JSF相关。

          为什么要再次访问数据库?您的 bean 已经包含您要在组件中显示的对象列表,还是请求范围?

          • 为您的 Hibernate Pojo 创建一个包含 id 字段的超类。如果您不想创建超类,只需使用 pojo 类,但您需要更多转换器类。
          • 使用该超类,您可以为所有 Pojo 类创建一个通用转换器,其中包含在构造函数中传递的 Pojo 列表。
          • 将转换器作为属性添加到会话 bean 中,并在 JSF 组件中使用该转换器。

          如果您通过 bean 中的 get 属性或转换器中的嵌套属性访问最终列表,您可以选择。

          public class SuperPojo
          {
              protected Integer id;
              //constructor & getter
          }
          
          public class PojoTest extends SuperPojo 
          {
              private String label = null;    
              //constructor & getter
          }
          
          public class SuperPojoConverter<T extends SuperPojo> implements Converter
          {
              private Collection<T> superPojos;
          
              public IdEntityConverter(Collection<T> superPojos)
              {
                  this.superPojos = superPojos;
              }
          
              @Override
              public Object getAsObject(FacesContext context, UIComponent component, String value)
              {
                  //catch exceptions and empty or  null value!
                  final int intValue = Integer.parseInt(value);
                  for(SuperPojo superPojo : this.superPojos)
                      if(superPojo.getId().intValue() == intValue)
                          return superPojo;
          
                  return null;
              }
          
              @Override
              public String getAsString(FacesContext context, UIComponent component, Object value)
              {
                  //catch null and instanceof
                  return String.valueOf(((SuperPojo)value).getId().intValue());
              }
          
              public Collection<T> getSuperPojos()
              {
                  return this.superPojos;
              }
          }
          
          public class Bean 
          {
              private SuperPojoConverter<PojoTest> pojoTestConverter = null;
          
              public Bean()
              {
                  final List<PojoTest> pojoTests = //get list from hibernate
                  this.pojoTestConverter = new SuperPojoConverter<PojoTest>(pojoTests);
              }
          
              public SuperPojoConverter<PojoTest> getPojoTestConverter()
              {
                  return this.pojoTestConverter;
              }
          }
          
          
          <h:selectOneMenu value="#{selPojoTest}" converter="#{bean.getPojoTestConverter}">
              <f:selectItems value="#{bean.getPojoTestConverter.getSuperPojos}" var="varPojoTest" itemLabel="#{varPojoTest.label}" itemValue="#{varPojoTest}"/>
          </h:selectOneMenu>
          

          【讨论】:

            【解决方案6】:

            有没有什么方法可以在没有 2 个数据库命中的情况下实现它?

            我的意思是,当你有

            #{bean.projects}
            

            这是一个数据库命中。

            当转换器放置

            sBean.getProjectById(value);
            

            是不必要的数据库命中,因为 bean.projects 已经具有对象的 id 和值

            【讨论】:

            • 有趣的问题。不幸的是,我真的不知道。我认为你应该提出这个问题,看看社区怎么说。如果你确实把它变成了一个帖子,请给我链接。我也想知道。
            猜你喜欢
            • 1970-01-01
            • 2011-05-13
            • 2021-03-25
            • 1970-01-01
            • 1970-01-01
            • 2017-06-22
            • 2010-10-21
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多