p:orderList converter getAsObject() doesn't call Object.toString()

旧时模样 提交于 2019-12-11 13:42:55

问题


I've written a custom converter as follows:

@FacesConverter(value = "orderListConverter")
public class OrderListConverter implements Converter {

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {

    Object ret = null;
    if (component instanceof OrderList) {
        Object list = ((OrderList) component).getValue();
        ArrayList<ExampleEntity> al = (ArrayList<ExampleEntity>) list;
        for (Object o : al) {
            String name = "" + ((ExampleEntity) o).getName();
            if (value.equals(name)) {
                ret = o;
                break;
            }
        }
    }
    return ret;
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) 
{
    String str = "";
    if (value instanceof ExampleEntity) {
        str = "" + ((ExampleEntity) value).getNumber();
    }
    return str;
}
}

My ExampleEntity is implemented as follows:

public class ExampleEntity {

private String name;

private int number;

public ExampleEntity(String name, int number) {
    this.name = name;
    this.number = number;
}

@Override
public String toString() {
    return "toString(): [name=" + name + ", number=" + number + "]";
}

public String getName() {
    return this.name;
}

public void setName(String name) {
    this.name = name;
}

public int getNumber() {
    return number;
}

public void setNumber(int number) {
    this.number = number;
}

}

the orderList-Component from Primefaces looks like that:

<p:orderList value="#{orderListBean.exampleList}"
var="exampleEntity" itemValue="#{exampleEntity}"
converter="orderListConverter">

    <p:column style="width:25%">
        #{exampleEntity.number}
    </p:column>
    <p:column style="width:75%;">
        #{exampleEntity.name}
    </p:column>
</p:orderList>

and the bean is implemented as follows:

@SessionScoped
@ManagedBean(name = "orderListBean")
public class OrderListBean {

private List<ExampleEntity> exampleList;

@PostConstruct
public void init() {

    exampleList = new ArrayList<ExampleEntity>();

    exampleList.add(new ExampleEntity("nameOne", 1));
    exampleList.add(new ExampleEntity("nameTwo", 2));
    exampleList.add(new ExampleEntity("nameThree", 3));
    exampleList.add(new ExampleEntity("nameFour", 4));
    exampleList.add(new ExampleEntity("nameFive", 5));

}

public List<ExampleEntity> getExampleList() {
    return exampleList;
}

public void setExampleList(List<ExampleEntity> exampleList) {
    this.exampleList = exampleList;
}

}

1) when debugging, the value Parameter of getAsObject() contains the number of the ExampleEntity, but I had expected the toString() method of ExampleEntity to be called!

2) What is the correct content for itemValue attribute? Is it a kind of convention over configuration? Or how does the component 'know', to use the whole object, when inserting exampleEntity into itemValue

Hope everything is clear! Tanks a lot for any explanation!


回答1:


Converters basically serve to transform values in 2 directions:

  1. Server to client, when the value is rendered.
  2. Client to server, when the value is submitted.

In your getAsString you established, that the string representation, the one which client uses, is exampleEntity's number. So that's what gets rendered to client as a value. And later, when the client submits its value, that value is number. To convert it to the object (server) representation, the getAsObject called with the number as a parameter.
The server can't possibly call getAsObject with exampleEntity.toString(), because it doesn't have the exampleEntity instance at that point, only the submitted number.

To illustrate, this should hold:

obj.equals(conv.getAsObject(ctx, comp, conv.getAsString(ctx, comp, obj)));

getAsObject and getAsString should be inversive in their input and output.

To answer your 2nd question: that depends on your needs. You could say itemValue="#{exampleEntity.number}", but that would make sense only if you're not interested in the exampleEntity itself, i.e. on submit you would get the number from the client and that's all you need for your server-side logic.




回答2:


@FacesConverter(value = "EntityConverter")
public class EntityConverter implements Converter, Serializable {

    private static final long serialVersionUID = 1L;

    public EntityConverter() {
        super();
    }

    @Override
    public Object getAsObject(final FacesContext context, final UIComponent component, final String value) {
        if (value == null) {
            return null;
        }
        return fromSelect(component, value);
    }

    /**
     * @param currentcomponent
     * @param objectString
     * @return the Object
     */
    private Object fromSelect(final UIComponent currentcomponent, final String objectString) {

        if (currentcomponent.getClass() == UISelectItem.class) {
            final UISelectItem item = (UISelectItem) currentcomponent;
            final Object value = item.getValue();
            if (objectString.equals(serialize(value))) {
                return value;
            }
        }

        if (currentcomponent.getClass() == UISelectItems.class) {
            final UISelectItems items = (UISelectItems) currentcomponent;
            final List<Object> elements = (List<Object>) items.getValue();
            for (final Object element : elements) {
                if (objectString.equals(serialize(element))) {
                    return element;
                }
            }
        }

        if (!currentcomponent.getChildren().isEmpty()) {
            for (final UIComponent component : currentcomponent.getChildren()) {
                final Object result = fromSelect(component, objectString);
                if (result != null) {
                    return result;
                }
            }
        }

        if (currentcomponent instanceof OrderList) {
            Object items = ((OrderList) currentcomponent).getValue();
            List<Object> elements = (List<Object>) items;
            for (final Object element : elements) {
                if (objectString.equals(serialize(element))) {
                    return element;
                }
            }
        }
        return null;
    }

    /**
     * @param object
     * @return the String
     */
    private String serialize(final Object object) {
        if (object == null) {
            return null;
        }
        return object.getClass() + "@" + object.hashCode();
    }

    @Override
    public String getAsString(final FacesContext arg0, final UIComponent arg1, final Object object) {
        return serialize(object);
    }
}



回答3:


Prueba con uno genérico para los orderList. Este funciona a la perfección.

@Override
public Object getAsObject(final FacesContext context, final UIComponent component, final String value) {
    if (value == null) {
        return null;
    }
    return fromSelect(component, value);
}


private Object fromSelect(final UIComponent currentcomponent, final String objectString) {


    if (currentcomponent instanceof OrderList) {
        final Object items = ((OrderList) currentcomponent).getValue();
        final List<Object> elements = (List<Object>) items;
        for (final Object element : elements) {
            if (objectString.equals(serialize(element))) {
                return element;
            }
        }
    }

    return null;
}

private String serialize(final Object object) {
    if (object == null) {
        return null;
    }
    return object.getClass() + "@" + object.hashCode();
}

@Override
public String getAsString(final FacesContext arg0, final UIComponent arg1, final Object object) {
    return serialize(object);
}


来源:https://stackoverflow.com/questions/30523944/porderlist-converter-getasobject-doesnt-call-object-tostring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!