Using this post as a reference I put together a bare bones Jersey controller method for POST calls that looks like this:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response createVisit(Visit newVisit) {
LOGGER.info("Creating visit");
this.visits.add(newVisit);
return Response.ok(newVisit)
.build();
}
Here are the fields on my Visit object (constructors and getter/setters omitted because I don't think they're relevant here - I can add them in if they'd be helpful):
public class Visit {
private VisitId id;
private AthleteId athleteId;
private CustomerId customerId;
private StoreId storeId;
private Instant createdUtc;
private Instant lastModifiedUtc;
}
When I pass in valid Visit object fields in JSON format in the request body, I see the Visit object successfully populated in the response as expected. However, if I add fields that aren't part of the Visit object to the request body they seem to be ignored.
I've seen a number of posts trying to figure out how to disable the FAIL_ON_UNKNOWN_PROPERTIES property, but I seem to be having the opposite issue. My understanding is that FAIL_ON_UNKNOWN_PROPERTIES defaults to true, but in that case I would expect to get an error response code (500?) when the JSON object in the request body doesn't match with the object I'm passing in to my POST method. Any ideas on what I'm missing here?
Use a ContextResolver, as mentioned in the documentation
#Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public MyObjectMapperProvider() {
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
}
Then you need to make sure the resolver is registered. If you are using some scanning mechanism to auto-register your resources and providers, this class should be picked up with the #Provider annotation
Related
Im trying to get the Json inside Json response of the rest api.
httpsConn.getInputStream() will be a Json like
"data":[
{"id":"1","name:"aaa","score":"90"},{"id":"2","name":"bbb","score":"85"}
]
Jave code:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
response = (MarkResponse)objectMapper.readValue(httpsConn.getInputStream(), MarkResponse.class);
Pojo class:
public class MarkResponse {
private int count;
private List<MarkData> markData;
//setter and getter.
}
public class MarkData {
private String id;
private String name;
}
The response is like below, as I'm using the List inside my main pojo.
{"headers":{},"body":"MarkResponse [count=2 markData=[MarkData [id=1,
name=aaa], MarkData
[id=2,name=bbb]]],"statusCode":"ACCEPTED","statusCodeValue":202}
What I'm expecting is,
{"headers":{},"body":"MarkResponse [count=2
markData={"id":"1","name:"aaa"},{"id":"2","name":"bbb"}],"statusCode":"ACCEPTED","statusCodeValue":202}
What is the code change i should make to get the expected output.
I think the problem is private fields. When I make fields public, jackson works and I don't need to objectMapper.disable(anything); when fields are private, or protected, or package-private, they are read, but not written to.
That is, assuming that you try to use org.codehaus.jackson.map.ObjectMapper (yes, not the latest version) rather than something else that defines a class named ObjectMapper.
This works after changing the return type.
My earlier response was,
public ResponseEntity<String> fnCall() {
//code here
return new ResponseEntity<String>(MarkResponse.toString(), HttpStatus.ACCEPTED);
}
I changed this to make it work.
public MarkResponse fnCall() {
//code here
return response;
}
public ResponseEntity<TastyDashResponse> order(#PathVariable("restaurantId") String restaurantId,
#RequestBody RestaurantOrderBook request,
#RequestBody ExpItems exp) {}
I have two Objects RestaurantOrderBook and ExpItems. Incoming request should have either of them and the other becomes optional.
How to achieve this within same method. When this code I get 404 bad request. please help with it.
You cannot use two #RequestBody as it can bind to a single object .
solution create one object that will capture all the relevent data like this this :
public class Data {
private String restaurantId;
private RestaurantOrderBook request;
private ExpItems exp;
getters/setters
}
public ResponseEntity<TastyDashResponse> order(#RequestBody Data data) {}
I'm using RoboSpice with Spring Android to perform REST api calls.
Data need to be sent with the Content-Type set to application/x-www-form-urlencoded.
Let's say I have a Pojo class like this:
public class Pojo {
private String pojoAttribute;
// getter + setter
}
Here is my request class:
public class PojoRequest extends AbstractApiRequest<PojoResult> {
private Pojo pojo;
public PojoRequest(Pojo pojo) {
super(PojoResult.class);
this.pojo = pojo;
}
#Override
public PojoResult loadDataFromNetwork() throws Exception {
HttpEntity<Pojo> requestEntity = new HttpEntity<Pojo>(pojo, getDefaultHeaders());
String url = buildUrlForPath("mypath");
RestTemplate restTemplate = getRestTemplate();
restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
return restTemplate.postForObject(url, requestEntity, PojoResult.class);
}
}
So, suppose I need to send this body:
pojo_attribute=foo
Now my code does not work because FormHttpMessageConverter does not handle POJOs.
What I would like to be able to do is something like this:
public class Pojo {
#FormProperty("pojo_attribute")
private String pojoAttribute;
// getter + setter
}
With a magic HttpMessageConverter that converts my POJO into key=value format taking care of converting the pojoAttribute to pojo_attribute by inspecting the #FormProperty (does not exist) annotation the same way Jackson does with #JsonProperty.
My questions are:
- is it possible to do this with existing classes/annotations?
- is there another way to do something similar?
- would it be overkill if I create my own HttpMessageConverter and set of annotations to do just that?
I need to get at the baseUri, I'm familliar with this
#Context
UriInfo uriInfo;
my problem is I need the baseUri in a piece of code I can't Inject into, (a sub object of an entity). I could pass uriInfo or the builder down but that doesn't seem ideal, as most of the objects have no business knowing about it. The end goal is HATEOAS URI's in my JSON responses, but I don't need it to be a JAX-RS specific solution, I figure if I can just get at the Base URI either in my jackson converters, or entities, I can create the URIBuilder from there.
The UriInfo is managed by the JAX-RS implementation and AFAIK, you cannot actually inject it in other places of your code (it's no a CDI bean). If your goal is to implement HATEOAS, you could probably write your own MessageBodyWriter which will be responsible for unmarshalling your entity objects graph into a JSON response and in which you can inject this UriInfo.
HTH.
UPDATE: Minutes after I wrote all this I found Chapter 12. Declarative Hyperlinking in the Jersey 2.13 spec. It's still "under development and experimental" but looks like it solves this problem much more elegantly. All the code in the original post can be replaced by this...
public class Pie {
private String name;
Pie(String name) {
this.name = name;
}
#JsonIgnore
public String getName() {
return name;
}
#InjectLink(value="pies/{name}", style=Style.ABSOLUTE)
public URI url;
}
If you need to send arbitrary data to a Jackson serializer then the solution below is still valid.
ORIGINAL I had the same issue. Specifically I wanted my resources to contain just their id (the thing at the end of their URI) and find a way to generate the complete URI using UriInfo only when Jackson generates the JSON. I wanted to avoid solutions where I had to either create new cover objects on my resources for each request, or insert the UriInfo into each resource before it was serialized.
It turns out that as of Jackson 2.3 there is a very nice way to do this. Jackson supports a class called ObjectWriterInjector. Using its static set method you can provide your own instance of an ObjectWriterModifier once per request. That instance can be created and set in your Jersey resource method where UriInfo is available. Before Jackson writes out JSON it calls your ObjectWriterModifier giving you a chance to tweak the ObjectWriter it will use. In this case, you can use the withAttribute method to attach UriInfo to the ObjectWriter.
So what good is an attribute on the ObjectWriter? It turns out that those attributes are available through the SerializerProvider parameter in the serialize method of a JsonSerializer implementation. In my case I only wanted to modify one field so I used #JsonSerialize on the getter. In the serialize method I then call the SerializerProvider parameter's getAttribute method to retrieve the UriInfo and then use it to construct the complete URI.
This methodology should work in any case where you want to push information down to a Jackson serializer.
Simple Jersey resource...
#Path("/pies")
public class Pies {
static List<Pie> pies;
static
{
pies = new ArrayList<Pie>();
pies.add(new Pie("apple"));
pies.add(new Pie("pumpkin"));
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Pie> getPies(#Context UriInfo uriInfo) {
// This is the good stuff!!!!
ObjectWriterInjector.set(new ObjectWriterPlusUriInfo(uriInfo));
// Notice I don't have to anything to the objects in the Pies list
return pies;
}
}
The class that tweaks the ObjectWriter...
class ObjectWriterPlusUriInfo extends ObjectWriterModifier {
private UriInfo uriInfo;
ObjectWriterPlusUriInfo(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
#Override
public ObjectWriter modify(EndpointConfigBase<?> endpoint,
MultivaluedMap<String, Object> responseHeaders, Object value,
ObjectWriter ow, JsonGenerator gen) throws IOException {
// Adding the UriInfo attribute
return ow.withAttribute("JerseyUriInfo", uriInfo);
}
}
The Pie class with Jackson annotations...
public class Pie {
private String name;
Pie(String name) {
this.name = name;
}
#JsonProperty(value="url")
#JsonSerialize(using=JacksonIdToUrlSerializer.class)
public String getName() {
return name;
}
}
Serializer...
public class JacksonIdToUrlSerializer extends JsonSerializer<String>{
#Override
public void serialize(String value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
UriInfo uriInfo = (UriInfo)provider.getAttribute("JerseyUriInfo");
jgen.writeString(uriInfo.getRequestUriBuilder().path(value).build().toString());
}
}
A GET of...
http://localhost:8080/service/api/pies...
Produces...
[
{
url: "http://localhost:8080/service/api/pies/apple"
},
{
url: "http://localhost:8080/service/api/pies/pumpkin"
}
]
A GET of...
http://127.0.0.1:8080/service/api/pies...
Produces...
[
{
url: "http://127.0.0.1:8080/service/api/pies/apple"
},
{
url: "http://127.0.0.1:8080/service/api/pies/pumpkin"
}
]
Note that the ObjectWriterInjector uses a Thread Local variable internally so using that more generic method to pass data down on a per request basis seems equally valid.
I am using Spring MVC with a controller like this:
#RequestMapping(value = "/list", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody List<Service> list() {
return services.list();
}
The model is like this:
public class Service {
private User user;
...
}
public class User {
private String name;
...
}
public class ExtendedUser extends User {
private Location location;
...
}
For sure, an object of type ExtendedUser is created in the application and set in Service. When the controller /list answer the request, an object of type ExtendedUser is serialized despite the reference in Service class is User. I would like to know if there is some way with annotations to only serialize supertype (the referenced type) and avoid the subtype propierties.
Taking the example into account, I want a JSON without the location property to be returned.
Thanks in advance
Try #JsonInclude(Include.NON_NULL) on ExtendedUser
Your statement of "I want a JSON without the location property to be returned" can easily be accomplished using the #JsonIngore annotation:
public class ExtendedUser extends User {
#JsonIgnore
private Location location;
...
}
Is that what you're trying to do, though, just eliminate the location from the response, or does the actual type returned (type id) matter? If I'm off base, please post your expected JSON result and your actual JSON result.
I think this will do the trick:
#JsonSerialize(using=User.class)
See this related answer: https://stackoverflow.com/a/13926740/1292605
I recommend to use as property of #JsonSerialize. BTW, #JsonSerialize can be declared on the fields so that it will not impact the common behavior of serialization of User or ExtendedUser.
public class Service {
#JsonSerialize(as = User.class)
private User user;
...
}