I have a page with an orderList component that is working partially.
This is the orderList
It has a list of names and also hold the ID for each system.
<p:orderList id="orderListAppList" value="#{manageApplications.listOfApplicationsAndIds}" var="app" controlsLocation="none" itemLabel="#{app.applicationName}" itemValue="#{app.applicationId}" >
<p:ajax event="select" listener="#{manageApplications.onSelect}"/>
<p:ajax event="unselect" listener="#{manageApplications.onUnselect}"/>
</p:orderList>
This is working as expected until I have added a button on the page that execute with ajax=false
When I click on this new button the error below is being thrown
javax.el.PropertyNotFoundException: Property 'applicationId' not found on type java.lang.String
This is the code that load the ArrayList used in the orderList component
#ManagedBean
#ViewScoped
public class ManageApplications implements Serializable {
private List<ApplicationNameAndId> listOfApplicationsAndIds = new ArrayList<ApplicationNameAndId>();
#PostConstruct
public void init() {
populateApplications();
}
private void populateApplications() {
// HERE I LOAD THE APPLICATION'S LIST FROM THE DATABASE
listOfApplicationsAndIds = manageDbApplications.getApplicationsListFromDatabase();
}
// GETTERS AND SETTERS
public List<ApplicationNameAndId> getListOfApplicationsAndIds() {
return listOfApplicationsAndIds;
}
public void setListOfApplicationsAndIds(List<ApplicationNameAndId> listOfApplicationsAndIds) {
this.listOfApplicationsAndIds = listOfApplicationsAndIds;
}
}
This is the type ApplicationNameAndId
public class ApplicationNameAndId implements Serializable {
private static final long serialVersionUID = -2031225157408650765L;
String applicationId;
String applicationName;
public ApplicationNameAndId() {
}
public ApplicationNameAndId(String appName, String appId) {
this.applicationId = appId;
this.applicationName = appName;
}
// GETTERS AND SETTERS
public String getApplicationId() {
return applicationId;
}
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
}
My point is.
The component is working normally until I use some button with ajax=false.
<p:commandButton actionListener="#{manageApplications.selectQueuesToAddToAnExistingApplication(qmgr.queueManagerNameId)}"
oncomplete="PF('dlgAddQueuesToApplication').show();"
update=":j_idt3:addQueuesToApplicationPanel"
value="ADD QUEUE" ajax=false icon="fa fa-plus">
</p:commandButton>
Note that the "button" that I am talking about has nothing do to with the oederList component. Just adding a button that execute a listener (or something) with ajax=fase generate this error.
Any idea why?
The issue was related to the object that I was using.
The Application on my application's list is a Class that can be represented this way:
public class ApplicationNameAndId {
String applicationId;
String applicationName;
// CONSTRUCTOR
// GETTERS AND SETTERS HERE
}
The orderList component does not deal with your own object type, this expect to receive a List of Strings instead.
List<String> ListOfStringToBeUsed
For now I have adapted my code to return the list of Strings as expected.
When having more time I will create a custom converter class (to use my own object type) than post the how to here.
I hope this helps someone else,
Neliosam
Related
I am working on an embedded jersey instance which will run a JAXB RESTful service. I have configured Jackson with two steps:
Adding this to my POM
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.23.2</version>
</dependency>
Registering it in my application
public HandheldApplication() {
scripts.add(HandheldServer.class);
scripts.add(BasicScript.class);
// Add JacksonFeature.
scripts.add(JacksonFeature.class);
scripts.add(LoggingFilter.class);
}
I have a complex object being passed back and forth as shown below:
package com.ziath.handheldserver.valueobjects;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.*;
#SuppressWarnings("restriction")
#XmlRootElement
public class Widget {
private String key;
private String name;
private List<String> options = new ArrayList<String>();
private String value;
private String type;
public Widget(){
super();
}
public Widget(String key, String name, List<String> options, String value,
String type) {
super();
this.key = key;
this.name = name;
this.options = options;
this.value = value;
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getOptions() {
return options;
}
public void setOptions(List<String> options) {
this.options = options;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
When I execute this in a GET method as shown below:
#Override
#GET
#Path("getKeys")
#Produces(MediaType.APPLICATION_JSON)
public List<Widget> getKeys(#QueryParam(value = "page") int page)
This works fine and I get JSON back; however when I execute it is a PUT as shown below:
#Override
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, #QueryParam(value = "widgets")List<Widget> widgets)
When I execute a PUT to access this method I get a stack trace as follows:
Caused by: org.glassfish.jersey.internal.inject.ExtractorException: Error un-marshalling JAXB object of type: class com.ziath.handheldserver.valueobjects.Widget.
at org.glassfish.jersey.jaxb.internal.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:195)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.convert(AbstractParamValueExtractor.java:139)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.fromString(AbstractParamValueExtractor.java:130)
at org.glassfish.jersey.server.internal.inject.CollectionExtractor.extract(CollectionExtractor.java:88)
at org.glassfish.jersey.server.internal.inject.CollectionExtractor$ListValueOf.extract(CollectionExtractor.java:107)
at org.glassfish.jersey.server.internal.inject.QueryParamValueFactoryProvider$QueryParamValueFactory.provide(QueryParamValueFactoryProvider.java:89)
... 38 more
Caused by: javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.]
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.createUnmarshalException(AbstractUnmarshallerImpl.java:335)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.createUnmarshalException(UnmarshallerImpl.java:563)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:249)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:214)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:140)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:123)
at org.glassfish.jersey.jaxb.internal.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:190)
... 43 more
So it seems to me that Jackson is correctly marshalling my POJO into JSON but trying to unmarshall it as XML. Note that I switched to Jackson away from MOXy because I needed to be able to handle collections coming back and forth and apparently MOXy cannot do that.
Is there a setting I've missed to tell Jackson/Jersey to go both ways for JSON?
Try removing #QueryParam(value = "widgets") because you should pass it as entity body - not query param.
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, List<Widget> widgets)
Also you can make wrapper class:
#XmlRootElement
public class Widgets {
private List<Widget> widgets;
// other fields, setters and getters
}
And then:
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, Widgets widgets)
I would suggest to read some discussions about REST design because you're using verbs in your paths:
Is this a bad REST URL?
Understanding REST: Verbs, error codes, and authentication
I was switching between QueryParam and FormParam to try and get one of them to work. If I use FormParam I also need to change the consumes to APPLICATION_FORM_URLENCODED.
The actual issue was that the default unmarshalling with Jackson was using XML because it was tagged as an XML resource - take that out! I finally managed to work out how to unmarshall from JSON by using a static fromString method. Then to handle the list; I cannot use a wrapper class because this needs to be highly cross language and exposing a wrapper with a list would have complicated the implementation from Python, C#, etc. The way to get it to accept a list with a wrapper is to post the name of the param (in this case widgets) multiple time. Then each JSON passed in will be called against the fromString method.
I've read countless articles about parsing Java objects to JSONs and still have issues...
I know that there are a bunch of frameworks out there and this is where things messed up I guess.
I'm trying to parse a map into a json:
Map<CategoryBean, Double> questionsPercentagePerCategory;
here's how CategoryBean looks like:
#XmlRootElement
public class CategoryBean implements Serializable{
private static final long serialVersionUID = -7306680546426636719L;
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
questionsPercentagePerCategory is a variable inside a wrapper json called: PrePracticeBean
and this is how it looks:
#XmlRootElement
public class PrePracticeBean implements Serializable {
private int maxQuestionsAllowedForUser;
private int maxQuestionsAllowedForUserAfterCreditOver;
private int questionsInExam;
#XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
private Map<CategoryBean, Double> questionsPercentagePerCategory;
private static final long serialVersionUID = -655358519739911024L;
public int getMaxQuestionsAllowedForUser() {
return maxQuestionsAllowedForUser;
}
public void setMaxQuestionsAllowedForUser(int maxQuestionsAllowedForUser) {
this.maxQuestionsAllowedForUser = maxQuestionsAllowedForUser;
}
public int getMaxQuestionsAllowedForUserAfterCreditOver() {
return maxQuestionsAllowedForUserAfterCreditOver;
}
public void setMaxQuestionsAllowedForUserAfterCreditOver(int maxQuestionsAllowedForUserAfterCreditOver) {
this.maxQuestionsAllowedForUserAfterCreditOver = maxQuestionsAllowedForUserAfterCreditOver;
}
public int getQuestionsInExam() {
return questionsInExam;
}
public void setQuestionsInExam(int questionsInExam) {
this.questionsInExam = questionsInExam;
}
public Map<CategoryBean, Double> getQuestionsPercentagePerCategory() {
return questionsPercentagePerCategory;
}
public void setQuestionsPercentagePerCategory(Map<CategoryBean, Double> questionsPercentagePerCategory) {
this.questionsPercentagePerCategory = questionsPercentagePerCategory;
}
}
as you can see I've marked both beans with #XmlRootElement annotation to get Jeresey's OOB bean to JSON parsing functionality as specified here
Furthermore, here's how the XMLGenericMapAdapter looks like:
public class XmlGenericMapAdapter<K, V> extends XmlAdapter<MapType<K, V>, Map<K, V>> {
#Override
public Map<K, V> unmarshal(MapType<K, V> orgMap) throws Exception {
HashMap<K, V> map = new HashMap<K, V>();
for (MapEntryType<K, V> mapEntryType : orgMap.getEntries()) {
map.put(mapEntryType.getKey(), mapEntryType.getValue());
}
return map;
}
#Override
public MapType<K, V> marshal(Map<K, V> v) throws Exception {
MapType<K, V> mapType = new MapType<K, V>();
for (Map.Entry<K, V> entry : v.entrySet()) {
MapEntryType<K, V> mapEntryType = new MapEntryType<K, V>();
mapEntryType.setKey(entry.getKey());
mapEntryType.setValue(entry.getValue());
mapType.getEntries().add(mapEntryType);
}
return mapType;
}
}
Well, the end result is what makes me crazy... it's intermittent... when running the code in debug mode, this works flawlessly showing a nested json for the map and each key:value pair is another nested json. However, when invoked in run mode, I get an ugly "memory address" instead of the CategoryBean key...
My only guess is that this is related to class loading matters and that I might be having some other JAR that's having a class which is loaded first in debug mode but not in run mode...
anyways, any suggestions as to how this should be done, would be appreciated.
thanks,
GBa.
Well, solved...
some JAXB/Jersey stuff which I'm not into understanding up to the last bit... but I bet that there's some JAXB guru out there who could give the right explanation why this is the case...
Anyways, bottom line is that the CategoryBean class should not have the annotation #XmlRootElement but instead should have #XmlAccessorType(XmlAccessType.FIELD)
I got the inspiration for that from Serializer for (Hash)Maps for Jersey use?
thanks,
GBa.
The thing is that I want to hide the null elements from a RESTFul JSON response (if it's possible).
The REST controller retrieves the information from a Mongo database and because this elements doesn't exist there I would like to ignore them when they are null.
This is my REST Controller (exposed with Jersey):
#Stateless
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
#Path(PropertiesRestURIConstants.PROPERTIES)
#Produces(MediaType.APPLICATION_JSON)
#RequestScoped
public class GetPropertiesController {
#EJB(mappedName = PropertiesManagerRemote.MAPPED_NAME)
PropertiesManagerRemote propertiesManager;
#GET
#Path(PropertiesRestURIConstants.PROPERTIES_ALL)
public List<PropertyEntity> getAllProperties() throws DBLayerException {
return propertiesManager.getAllProperties();
}
...
...
...
}
This is my entity:
#Document(collection = "property")
public class PropertyEntity implements GenericEntity {
#Id
private String id;
private String propertyName;
private String propertyValue;
public PropertyEntity() {
}
public PropertyEntity(String propertyName, String propertyValue) {
this.propertyName = propertyName;
this.propertyValue = propertyValue;
}
...
...
...
}
And this is the result:
[{"id":"542c00c2ff5e0ba4ea58790d","propertyName":"property1","propertyValue":null},{"id":"542c00c2ff5e0ba4ea58790e","propertyName":"property2","propertyValue":null},{"id":"542c00c2ff5e0ba4ea58790f","propertyName":"property3","propertyValue":null}]
I use Spring Data for the persistence layer. I tried with JSONIgnore annotations and similar things, but nothing works for me.
Any help will be welcome.
Thanks in advance.
Try to annotate it this way:
#JsonInclude(Include.NON_EMPTY)
public class PropertyEntity implements GenericEntity {
On my server I have complex objects but in my web client I would like to get a simple representation of these objects.
I would like to replace object with their id for example:
#JsonAutoDetect
public class Event{
private Long id;
private String name;
private User user;
#JsonProperty("event_id")
public Long getId() {
return event_id;
}
public String getName() {
return name;
}
public String getUser() {
return user;
}
....setters
}
When generating Json I will get Event class and user class inside
Is there anyway to replace the User with user id?
Since I have many complex objects I am looking for an elegant way like maybe an annotation over the User object that will return only the object id.
Ofcourse I will need the other way around. getting the id and transforming it to the object.
BTW: I'm using Jackson
Any Ideas?
10x
Noam
You can expose a property for Id and ignore the sub-entity property like this:
class Event {
private User user;
public Long getUserId() {
return user.getId();
}
public void setUserId(Long userId) {
this.user = retrieveUserById(userId); // or any other code
}
#JsonIgnore
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
Here is my idea how to achieve it. You can use #JsonRawValue(true) annotation on your user property. This will make Jackson treat user object as already serialized stuff. And then just override .toString() method for User class to make it return it's id as a string.
So in your Event class:
public class Event {
...
#JsonRawValue(true)
private User user;
...
}
And in your User class:
public class User {
...
private Integer id;
...
#Override
public String toString() {
return id.toString();
}
...
}
This will result in e.g. "user": 5 in your json (no double quotes will be added around User object serizlized value).
In a previous similar question, I asked about, how to serialise two different sets of fields using JacksonJson and Spring.
My use case is the typical Controller mapping with #ResponseBody annotation returning directly a particular object or collections of objects, that are then rendered with JacksonJson whenever the client adds application/json in the Accept header.
I had two answers, the first one suggests to return different interfaces with a different getter list, the second suggests to use Json Views.
I don't have problems to understand the first way, however, for the second, after reading the documentation on JacksonJsonViews, I don't know how to implement it with Spring.
To stay with the example, I would declare three stub classes, inside the class Views:
// View definitions:
public class Views {
public static class Public { }
public static class ExtendedPublic extends PublicView { }
public static class Internal extends ExtendedPublicView { }
}
Then I've to declare the classes mentioned:
public class PublicView { }
public class ExtendedPublicView { }
Why on earth they declare empty static classes and external empty classes, I don't know. I understand that they need a "label", but then the static members of Views would be enough. And it's not that ExtendedPublic extends Public, as it would be logical, but they are in fact totally unrelated.
And finally the bean will specify with annotation the view or list of views:
//changed other classes to String for simplicity and fixed typo
//in classname, the values are hardcoded, just for testing
public class Bean {
// Name is public
#JsonView(Views.Public.class)
String name = "just testing";
// Address semi-public
#JsonView(Views.ExtendedPublic.class)
String address = "address";
// SSN only for internal usage
#JsonView(Views.Internal.class)
String ssn = "32342342";
}
Finally in the Spring Controller, I've to think how to change the original mapping of my test bean:
#RequestMapping(value = "/bean")
#ResponseBody
public final Bean getBean() {
return new Bean();
}
It says to call:
//or, starting with 1.5, more convenient (ObjectWriter is reusable too)
objectMapper.viewWriter(ViewsPublic.class).writeValue(out, beanInstance);
So I have an ObjectMapper instance coming out of nowhere and an out which is not the servlet typical PrintWriter out = response.getWriter();, but is an instance of JsonGenerator and that can't be obtained with the new operator. So I don't know how to modify the method, here is an incomplete try:
#RequestMapping(value = "/bean")
#ResponseBody
public final Bean getBean() throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonGenerator out; //how to create?
objectMapper.viewWriter(Views.Public.class).writeValue(out, new Bean());
return ??; //what should I return?
}
So I would like to know if anybody had success using JsonView with Spring and how he/she did. The whole concept seems interesting, but the documentation seems lacking, also the example code is missing.
If it's not possible I will just use interfaces extending each others. Sorry for the long question.
Based on the answers by #igbopie and #chrislovecnm, I've put together an annotation driven solution:
#Controller
public class BookService
{
#RequestMapping("/books")
#ResponseView(SummaryView.class)
public #ResponseBody List<Book> getBookSummaries() {}
#RequestMapping("/books/{bookId}")
public #ResponseBody Book getBook(#PathVariable("bookId") Long BookId) {}
}
Where SummaryView is annotated on the Book model like so:
#Data
class Book extends BaseEntity
{
#JsonView(SummaryView.class)
private String title;
#JsonView(SummaryView.class)
private String author;
private String review;
public static interface SummaryView extends BaseView {}
}
#Data
public class BaseEntity
{
#JsonView(BaseView.class)
private Long id;
}
public interface BaseView {}
A custom HandlerMethodReturnValueHandler is then wired into Spring MVC's context to detect the #ResponseView annotation, and apply the Jackson view accordingly.
I've supplied full code over on my blog.
You need to manually wire in the MappingJacksonHttpMessageConverter. In spring 3.1 you are able to use the mvc xml tags like the following:
<mvc:annotation-driven >
<mvc:message-converter>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
It is pretty ugly to not use spring 3.1, it will save you about 20 lines of xml. The mvc:annotation tag does ALOT.
You will need to wire in the object mapper with the correct view writer. I have noticed recently the using a #Configuration class can make complicated wiring like this a lot easier. Use a #Configuration class and create a #Bean with your MappingJacksonHttpMessageConverter, and wire the reference to that bean instead of the MappingJacksonHttpMessageConverter above.
I've manage to solve the problem this way:
Create custom abstract class to contain the json response object:
public abstract AbstractJson<E>{
#JsonView(Views.Public.class)
private E responseObject;
public E getResponseObject() {
return responseObject;
}
public void setResponseObject(E responseObject) {
this.responseObject = responseObject;
}
}
Create a class for each visibility (just to mark the response):
public class PublicJson<E> extends AbstractJson<E> {}
public class ExtendedPublicJson<E> extends AbstractJson<E> {}
public class InternalJson<E> extends AbstractJson<E> {}
Change your method declaration:
#RequestMapping(value = "/bean")
#ResponseBody
public final PublicJson<Bean> getBean() throws JsonGenerationException, JsonMappingException, IOException {
return new PublicJson(new Bean());
}
Create customs MessageConverter:
public class PublicJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public PublicApiResponseMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.Public.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(PublicJson.class)){
return true;
}
return false;
}
}
public class ExtendedPublicJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public ExtendedPublicJsonMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.ExtendedPublic.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(ExtendedPublicJson.class)){
return true;
}
return false;
}
}
public class InternalJsonMessageConverter extends MappingJacksonHttpMessageConverter{
public InternalJsonMessageConverter(){
super();
org.codehaus.jackson.map.ObjectMapper objMapper=new org.codehaus.jackson.map.ObjectMapper();
objMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
objMapper.setSerializationConfig(objMapper.getSerializationConfig().withView(Views.Internal.class));
this.setObjectMapper(objMapper);
}
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
if(clazz.equals(Internal.class)){
return true;
}
return false;
}
}
Add the following to your xml:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="PublicJsonMessageConverter"></bean>
<bean class="ExtendedPublicJsonMessageConverter"></bean>
<bean class="InternalJsonMessageConverter"></bean>
</mvc:message-converters>
</mvc:annotation-driven>
That's it! I had to update to spring 3.1 but that's all. I use the responseObject to send more info about the json call but you can override more methods of the MessageConverter to be completely transparent. I hope someday spring include an annotation for this.
Hope this helps!