Jersey 2.6 @QueryParam is missing from @BeanParam when using MOXY but not with Jackson

StackOverflow https://stackoverflow.com/questions/22106908

  •  18-10-2022
  •  | 
  •  

سؤال

I am developing a set of REST web services for my company. We are trying to settle on what technologies we want to use. I have a template web service that works with JAX-RS Jersey 2.6 when I use the Jackson JSON providers, but doesn't seem to marshal the @QueryParam correctly when I use the Moxy providers.

The search may include multiple "types" such as type=keyword&type=product_number&type=fubar These are mapped to the List types which contains all of the "type" QueryParam. When I build the project with Jackson, the values of type are correctly collected into the List, when I use MOXy the List is null. MOXy does map all of the other Query and Path Params in the BeanParam.

The problem seems to be in how JERSEY is When I use Jackson the service works great:

http://XXX:8080/SearchTermJersey/search/1/as/wat?type=product_number&type=keyword&count=4&lang=en_US

This is the JSON it returned:

{"autoSuggestions":{"product_number":{"<span>wat</span>21000":34},"keyword":{"<span>wat</span>er":100,"<span>wat</span>er solution":50,"<span>wat</span>er purity":100}},"language":"en_US","requestDate":1393623225135,"responseDate":1393623225135,"term":"wat","version":"1"}

The URL for the Moxy version of the service returns:

{"language":"en_US","requestDate":1393622174166,"responseDate":1393622174166,"term":"wat","version":"1"}

The Java code is identical between the MOXy and Jackson versions This is the BeanParam:

public class AutoSuggestParam {
@PathParam("version")
private String version;

@PathParam("term")
private String term;

private List<String>types;

private Integer count;

String language;

public AutoSuggestParam(@QueryParam("count")int count, @QueryParam("type")List<String>types, @QueryParam("lang")String language) {
    this.types = types;
    this.count = count;
    this.language = language;
}
public String getVersion() {
    return version;
}

public void setVersion(String version) {
    this.version = version;
}

public String getTerm() {
    return term;
}

public void setTerm(String term) {
    this.term = term;
}


public Integer getCount() {
    return count;
}


public String getLanguage() {
    return language;
}

public List<String>getTypes() {
    return types != null ? types : new ArrayList<String>();
}

The problem seems to be in how the types parameter is handled. With Jackson the types QueryParams are correctly marshalled into the List, but MOXy fails and just returns a null. So getTypes is returning an empty List. The simple QueryParam count and lang are handled correctly. Is this a bug in Jersey or do I need to do something else with MOXy?

Here is my Resource class:

@javax.ws.rs.Path("/search/{version}/as/{term}")
public class AutoSuggestResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public AutoSuggestResponse getAutoSuggest(@BeanParam AutoSuggestParam autoSuggestParam) {
    System.out.printf("Request: term=%s version=%s lang=%s type=%s count=%d%n", 
            autoSuggestParam.getTerm(),autoSuggestParam.getVersion(), autoSuggestParam.getLanguage(), autoSuggestParam.getTypes().get(0), autoSuggestParam.getCount());
    return search(autoSuggestParam);
}



private AutoSuggestResponse search(AutoSuggestParam autoSuggestParam) {
    AutoSuggestResponse autoSuggestResponse = new AutoSuggestResponse();
    autoSuggestResponse.setRequestDate(new Date().getTime());
    autoSuggestResponse.setVersion(autoSuggestParam.getVersion());
    autoSuggestResponse.setTerm(autoSuggestParam.getTerm());
    autoSuggestResponse.setLanguage(autoSuggestParam.getLanguage());
    int cnt = 0;
    for (String type : autoSuggestParam.getTypes()) {
        if ("product_number".equals(type)) {
            Map<String, Object> values = autoSuggestResponse.getAutoSuggestions().get(type);
            if (values == null) {
                values = new LinkedHashMap<String, Object>();
                autoSuggestResponse.getAutoSuggestions().put(type, values);
            }
            String key = String.format("<span>%s</span>21000", autoSuggestParam.getTerm());
            values.put(key, 34);
            cnt++;
        }
        else if ("keyword".equals(type)) {
            Map<String, Object> values = autoSuggestResponse.getAutoSuggestions().get(type);
            if (values == null) {
                values = new LinkedHashMap<String, Object>();
                autoSuggestResponse.getAutoSuggestions().put(type, values);
            }
            String key = String.format("<span>%s</span>er", autoSuggestParam.getTerm());
            values.put(key, 100);
            cnt++;
            key = String.format("<span>%s</span>er solution", autoSuggestParam.getTerm());
            values.put(key, 50);
            cnt++;
            key = String.format("<span>%s</span>er purity", autoSuggestParam.getTerm());
            values.put(key, 100);
            cnt++;

        }
        if (cnt >= autoSuggestParam.getCount()) {
            break;
        }
    }
    autoSuggestResponse.setResponseDate(new Date().getTime());
    return autoSuggestResponse;
}

The Response class:

public class AutoSuggestResponse {
private Long requestDate;
private Long responseDate;
private String version;
private String term;
private String language;

private Map<String, Map<String,Object>>autoSuggestions = new LinkedHashMap<String, Map<String,Object>>();

public Long getRequestDate() {
    return requestDate;
}
public void setRequestDate(Long requestDate ) {
    this.requestDate = requestDate;
}
public Long getResponseDate() {
    return responseDate;
}
public void setResponseDate(Long responseDate) {
    this.responseDate = responseDate;
}
public String getVersion() {
    return version;
}
public void setVersion(String version) {
    this.version = version;
}
public String getTerm() {
    return term;
}
public void setLanguage(String language) {
    this.language = language;
}
public String getLanguage() {
    return language;
}
public void setTerm(String term) {
    this.term = term;
}
public Map<String, Map<String,Object>>getAutoSuggestions() {
    return autoSuggestions;
}

}

The web.xml

      <display-name>MoxyAS</display-name>
<servlet>
<servlet-name>MoxyAutoSuggest</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
  <param-name>jersey.config.server.provider.packages</param-name>
  <param-value>com.sial.search.ws</param-value>
</init-param>
<load-on-startup>1</load-on-startup>

  <servlet-mapping>
<servlet-name>MoxyAutoSuggest</servlet-name>
<url-pattern>/*</url-pattern>
 </servlet-mapping>
</web-app>
هل كانت مفيدة؟

المحلول

By default EclipseLink JAXB (MOXy) will not marshal properties that only have a getter. You can add an @XmlElement annotation to have it become mapped:

@XmlElement
public Map<String, Map<String,Object>>getAutoSuggestions() {
    return autoSuggestions;
}

By default MOXy does not use the map key as the JSON key. Below is a link to an example that explains how to set this up:

نصائح أخرى

I figured out the problem. It had nothing to do with MOXy, adding the genson-0.98.jar to the path fixed the problem with the QueryParam not getting marshaled in the BeanParam.

Adding the @XmlElement to the resource did make Moxy work, sort of. If I add XmlElement to the Map in the Response Class I now get an error: javax.servlet.ServletException: org.glassfish.jersey.server.ContainerException: java.lang.NoClassDefFoundError: org/eclipse/persistence/internal/libraries/asm/ClassWriter

But this is a new problem.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top