Reputation: 1608
I'm attempting to move to Jersey 2.0. Which is giving me pains with Jackson, and the docs recommend using Moxy.
I got Moxy working for get and post calls where everything matches nicely however I have a need to deal with possible unknown elements.
// Handle unknown deserialization parameters
@JsonAnySetter
protected void handleUnknown(String key, Object value) {
if (unknownParameters == null) {
unknownParameters = new HashMap<>();
}
unknownParameters.put(key, value);
}
This worked well with prior to changing to jersey 2.0 and even though it doesn't cause any problems when I leave it in it never gets called.
How do I implement this in Jersey 2.0? I'm fine with Moxy or Jackson.
my dependencies
'org.codehaus.jackson:jackson-mapper-asl:1.9.2',
'org.eclipse.persistence:org.eclipse.persistence.moxy:2.5.0',
'org.glassfish.jersey.media:jersey-media-moxy:2.0',
This has no effect in my web.xml
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Upvotes: 3
Views: 2980
Reputation: 149037
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
MOXy does not have a direct equivalent to Jackson's @JsonAnySetter
. I have entered the following enhancement request to add this type of behaviour:
Below are some information on some MOXy's extensions that may apply to your use case.
@XmlVirtualAccessMethods
If the JSON properties aren't really unknown, they just don't exist as properties on your domain model then you can use MOXy's @XmlVirtualAccessMethods
extension (see: http://blog.bdoughan.com/2011/06/extensible-models-with-eclipselink-jaxb.html).
Java Model
Customer
The @XmlVirtualAccess
methods annotation is used to specify that the Customer
class is extensible.
import java.util.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;
@XmlVirtualAccessMethods(setMethod = "put")
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private String firstName;
private Address billingAddress;
@XmlTransient
private Map<String, Object> extensions = new HashMap<String, Object>();
public <T> T get(String property) {
return (T) extensions.get(property);
}
public void put(String property, Object value) {
extensions.put(property, value);
}
}
Address
package forum18068176;
public class Address {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
Mapping Document (oxm.xml)
The definitions for the extension properties are defined in MOXy's mapping document (see: http://blog.bdoughan.com/2011/04/moxys-xml-metadata-in-jax-rs-service.html).
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum18068176">
<java-types>
<java-type name="Customer">
<xml-type prop-order="firstName lastName billingAddress shippingAddress"/>
<java-attributes>
<xml-element
java-attribute="lastName"
type="java.lang.String"/>
<xml-element
java-attribute="shippingAddress"
type="forum18068176.Address"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Demo
Below is some standalone demo code you can run to see how everything works. You will need to specify MOXy as your JAXB provider (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
package forum18068176;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum18068176/oxm.xml");
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
// Unmarshal JSON
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum18068176/input.json");
Customer customer = unmarshaller.unmarshal(json, Customer.class).getValue();
// Access Extension Properties
String lastName = customer.<String>get("lastName");
Address shippingAddress = customer.<Address>get("shippingAddress");
// Marshal Objects
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
input.json/Output
{
"firstName" : "Jane",
"lastName" : "Doe",
"billingAddress" : {
"street" : "1 A Street"
},
"shippingAddress" : {
"street" : "2 B Road"
}
}
XmlAdapter
If all of the unknown items are of the same type then you can use a combination of MOXy's @XmlVariableNode
(see: http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-json-schema.html) and XmlAdapter
to get the desired result:
Upvotes: 5
Reputation: 10379
You can use jersey-media-json-jackson
module instead of MOXy to take advantage of the annotation (I am not sure whether MOXy has support for a similar feature). Just few notes:
com.sun.jersey.api.json.POJOMappingFeature
init parameter from your web.xml
- Jersey 2.x does not recognize this property (it's Jersey 1.x specific)org.glassfish.jersey.media:jersey-media-moxy:2.0
dependencyorg.glassfish.jersey.media:jersey-media-json-jackson:2.0
dependency and register JacksonFeature in your application (see below)After these steps Jackson should be handling Object<->JSON (un)marshalling and it should recognize @JsonAnySetter
.
To register JacksonFeature
in your application, see dedicated section in users guide (8.1.4 Jackson):
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.jackson")
.register(MyObjectMapperProvider.class) // No need to register this provider if no special configuration is required.
.register(JacksonFeature.class);
Upvotes: 8
Reputation: 116572
Jersey 2.0 should work fine with unmodified Jackson 2.x JAX-RS providers from https://github.com/FasterXML/jackson-jaxrs-providers. There is no need to configure anything via web.xml, as provider uses Service Provider Interface. The only potential problem would come from having multiple providers for same media type.
Just make sure to use 2.x (like 2.2.2) versions of Jackson dependencies. While Jackson 1.x and 2.x versions can co-exist, dependencies between modules must match (that is, 2.2 JAX-RS provider depends on 2.2 jackson-core and jackson-databind jars).
Upvotes: 2